countable-inflections 0.2.0 → 0.3.0
raw patch · 5 files changed
+102/−46 lines, 5 filesdep ~QuickCheckdep ~basedep ~exceptionsPVP ok
version bump matches the API change (PVP)
Dependency ranges changed: QuickCheck, base, exceptions, hspec, text
API changes (from Hackage documentation)
+ Text.Countable: inflect :: Text -> Int -> Text
+ Text.Countable: inflectWith :: [Inflection] -> Text -> Int -> Text
Files
- CHANGELOG.md +11/−0
- README.md +13/−1
- Text/Countable.hs +53/−35
- countable-inflections.cabal +10/−10
- test/Text/CountableSpec.hs +15/−0
CHANGELOG.md view
@@ -0,0 +1,11 @@+## 0.3.0++* Bumped lts from 8.0 to 10.8++## 0.2.1++* Add inflect and inflectWith methods for pluralizing or singularizing based on input++## 0.2.0++* solidify basic API and fix regex problems with replace
README.md view
@@ -41,7 +41,7 @@ λ: :t makeMatchMapping [(RegexPattern, RegexReplace)] -> [Inflection] -λ: let mapping = makeMatchMapping [("(octop)us", "\1i")]+λ: let mapping = makeMatchMapping [("(octop)us", "\\1i")] λ: pluralizeWith mapping "octopus" "octopi" ```@@ -67,6 +67,18 @@ [Text] -> [Inflection] ``` +### Inflect++In general you can input a number and singularize or pluralize based on the count, for example:++```haskell+setReport = do+ sets <- getSets+ n <- length sets+ print $ show n ++ " " ++ inflect "sets" n+```++This way it'll list as "1 set" or "5 sets" based on the input. ## License
Text/Countable.hs view
@@ -3,11 +3,15 @@ -- Copyright : © 2016 Brady Ouren -- License : MIT ----- Maintainer : Brady Ouren <brady@andand.co>--- Stability : experimental+-- Maintainer : Brady Ouren <brady.ouren@gmail.com>+-- Stability : stable -- Portability : portable -- -- pluralization and singularization transformations+--+-- Note: This library is portable in the sense of haskell extensions+-- however, it is _not_ portable in the sense of requiring PCRE regex+-- bindings on the system {-# LANGUAGE OverloadedStrings #-} @@ -16,39 +20,39 @@ , pluralizeWith , singularize , singularizeWith+ , inflect+ , inflectWith , makeMatchMapping , makeIrregularMapping , makeUncountableMapping ) where -import Data.Maybe (catMaybes, fromMaybe, isNothing)-import Data.Text as T+import Data.Maybe (catMaybes, fromMaybe, isJust)+import Data.Text (Text) import Data.Text.Encoding (decodeUtf8, encodeUtf8) import Text.Countable.Data import Text.Regex.PCRE.ByteString import Text.Regex.PCRE.ByteString.Utils (substitute') import System.IO.Unsafe -type RegexPattern = T.Text-type RegexReplace = T.Text-type Singular = T.Text-type Plural = T.Text+type RegexPattern = Text+type RegexReplace = Text+type Singular = Text+type Plural = Text data Inflection = Simple (Singular, Plural) | Match (Maybe Regex, RegexReplace) -data MatchType = HasMatch | HasNoMatch- -- | pluralize a word given a default mapping-pluralize :: T.Text -> T.Text+pluralize :: Text -> Text pluralize = pluralizeWith mapping where mapping = defaultIrregulars ++ defaultUncountables ++ defaultPlurals -- | singularize a word given a default mapping-singularize :: T.Text -> T.Text+singularize :: Text -> Text singularize = singularizeWith mapping where mapping = defaultIrregulars ++ defaultUncountables ++ defaultSingulars@@ -56,23 +60,36 @@ -- | pluralize a word given a custom mapping. -- Build the [Inflection] with a combination of -- `makeUncountableMapping` `makeIrregularMapping` `makeMatchMapping`-pluralizeWith :: [Inflection] -> T.Text -> T.Text-pluralizeWith mapping t = fromMaybe t $ headMaybe matches- where- matches = catMaybes $ fmap (pluralLookup t) (Prelude.reverse mapping)+pluralizeWith :: [Inflection] -> Text -> Text+pluralizeWith = lookupWith pluralLookup -- | singularize a word given a custom mapping. -- Build the [Inflection] with a combination of -- `makeUncountableMapping` `makeIrregularMapping` `makeMatchMapping`-singularizeWith :: [Inflection] -> T.Text -> T.Text-singularizeWith mapping t = fromMaybe t $ headMaybe matches+singularizeWith :: [Inflection] -> Text -> Text+singularizeWith = lookupWith singularLookup++-- | inflect a word given any number+inflect :: Text -> Int -> Text+inflect t i = case i of+ 1 -> singularize t+ _ -> pluralize t++-- | inflect a word given any number and inflection mapping+inflectWith :: [Inflection] -> Text -> Int -> Text+inflectWith l t i = case i of+ 1 -> singularizeWith l t+ _ -> pluralizeWith l t++lookupWith :: (Text -> Inflection -> Maybe Text) -> [Inflection] -> Text -> Text+lookupWith f mapping target = fromMaybe target $ headMaybe matches where- matches = catMaybes $ fmap (singularLookup t) (Prelude.reverse mapping)+ matches = catMaybes $ fmap (f target) (Prelude.reverse mapping) -- | Makes a simple list of mappings from singular to plural, e.g [("person", "people")] -- the output of [Inflection] should be consumed by `singularizeWith` or `pluralizeWith` makeMatchMapping :: [(RegexPattern, RegexReplace)] -> [Inflection]-makeMatchMapping = fmap (\(pattern, rep) -> Match (regexPattern pattern, rep))+makeMatchMapping = fmap (\(pat, rep) -> Match (regexPattern pat, rep)) -- | Makes a simple list of mappings from singular to plural, e.g [("person", "people")] -- the output of [Inflection] should be consumed by `singularizeWith` or `pluralizeWith`@@ -82,8 +99,8 @@ -- | Makes a simple list of uncountables which don't have -- singular plural versions, e.g ["fish", "money"] -- the output of [Inflection] should be consumed by `singularizeWith` or `pluralizeWith`-makeUncountableMapping :: [T.Text] -> [Inflection]-makeUncountableMapping = fmap (\a -> (Simple (a,a)))+makeUncountableMapping :: [Text] -> [Inflection]+makeUncountableMapping = fmap (\a -> Simple (a,a)) defaultPlurals :: [Inflection]@@ -98,32 +115,33 @@ defaultUncountables :: [Inflection] defaultUncountables = makeUncountableMapping defaultUncountables' -pluralLookup :: T.Text -> Inflection -> Maybe T.Text+pluralLookup :: Text -> Inflection -> Maybe Text pluralLookup t (Match (r1,r2)) = runSub (r1,r2) t-pluralLookup t (Simple (a,b)) = if t == a then (Just b) else Nothing+pluralLookup t (Simple (a,b)) = if t == a then Just b else Nothing -singularLookup :: T.Text -> Inflection -> Maybe T.Text+singularLookup :: Text -> Inflection -> Maybe Text singularLookup t (Match (r1,r2)) = runSub (r1,r2) t-singularLookup t (Simple (a,b)) = if t == b then (Just a) else Nothing+singularLookup t (Simple (a,b)) = if t == b then Just a else Nothing -runSub :: (Maybe Regex, RegexReplace) -> T.Text -> Maybe T.Text+runSub :: (Maybe Regex, RegexReplace) -> Text -> Maybe Text runSub (Nothing, _) _ = Nothing runSub (Just reg, rep) t = matchWithReplace (reg, rep) t -matchWithReplace :: (Regex, RegexReplace) -> T.Text -> Maybe T.Text-matchWithReplace (reg, rep) t = case regexMatch t reg of- HasNoMatch -> Nothing- HasMatch -> toMaybe $ substitute' reg (encodeUtf8 t) (encodeUtf8 rep)+matchWithReplace :: (Regex, RegexReplace) -> Text -> Maybe Text+matchWithReplace (reg, rep) t =+ if regexMatch t reg+ then toMaybe $ substitute' reg (encodeUtf8 t) (encodeUtf8 rep)+ else Nothing where toMaybe = either (const Nothing) (Just . decodeUtf8) -regexMatch :: T.Text -> Regex -> MatchType+regexMatch :: Text -> Regex -> Bool regexMatch t r = case match of- Left _ -> HasNoMatch- Right m -> if isNothing m then HasNoMatch else HasMatch+ Left _ -> False+ Right m -> isJust m where match = unsafePerformIO $ execute r (encodeUtf8 t) -regexPattern :: T.Text -> Maybe Regex+regexPattern :: Text -> Maybe Regex regexPattern pat = toMaybe reg where toMaybe = either (const Nothing) Just reg = unsafePerformIO $ compile compCaseless execBlank (encodeUtf8 pat)
countable-inflections.cabal view
@@ -1,5 +1,5 @@ name: countable-inflections-version: 0.2.0+version: 0.3.0 synopsis: Countable Text Inflections description: Provides methods for singularizing and pluralizing text.@@ -7,10 +7,10 @@ license: MIT license-file: LICENSE.md-author: Brady Ouren <brady@andand.co>+author: Brady Ouren <brady.ouren@gmail.com> homepage: https://github.com/tippenein/countable-inflections bug-reports: https://github.com/tippenein/countable-inflections/issues-maintainer: Brady Ouren <brady@andand.co>+maintainer: Brady Ouren <brady.ouren@gmail.com> copyright: 2016 Brady Ouren category: Text build-type: Simple@@ -32,9 +32,9 @@ other-modules: Text.Countable.Data ghc-options: -Wall build-depends:- base >= 4.6 && < 4.10- , exceptions >= 0.6 && < 0.9- , text >= 0.2 && < 1.3+ base >= 4.8 && < 5+ , exceptions+ , text , regex-pcre-builtin , pcre-utils , bytestring@@ -46,10 +46,10 @@ main-is: Spec.hs build-depends: countable-inflections- , QuickCheck >= 2.7.6 && < 3.0- , base >= 4.2 && < 4.10- , hspec >= 2.0 && < 3.0- , text >= 0.2 && < 1.3+ , QuickCheck+ , base+ , hspec+ , text ghc-options: -Wall default-language: Haskell2010 other-modules: Text.CountableSpec
test/Text/CountableSpec.hs view
@@ -10,6 +10,21 @@ spec = do irregularCases matchingCases+ inflectingCases++inflectingCases :: Spec+inflectingCases = do+ describe "given 1" $ do+ it "singularizes" $+ inflect "sets" 1 `shouldBe` "set"++ describe "given 10" $ do+ it "pluralizes" $+ inflect "sets" 10 `shouldBe` "sets"++ it "pluralizes when in doubt" $+ inflect "sets" (-1) `shouldBe` "sets"+ matchingCases :: Spec matchingCases = do