git.iko.soy/typeable/inflections-hs
on master help sign in
.travis.yml+17−26
since splitsnapshots
unifiedsplit
@@ -2,21 +2,21 @@ language: c
2
2
3sudo: false
3sudo: false
4
4
5+cache:
6+ directories:
7+ - ~/.ghc
8+ - ~/.cabal
9+
5matrix:
10matrix:
6 include:
11 include:
7− - env: CABALVER=1.16 GHCVER=7.6.3
12+ - env: CABALVER=1.24 GHCVER=7.10.3
8− addons: {apt: {packages: [cabal-install-1.16,ghc-7.6.3], sources: [hvr-ghc]}}
13+ addons: {apt: {packages: [cabal-install-1.24,ghc-7.10.3],sources: [hvr-ghc]}}
9− - env: CABALVER=1.18 GHCVER=7.8.4
14+ - env: CABALVER=1.24 GHCVER=8.0.2
10− addons: {apt: {packages: [cabal-install-1.18,ghc-7.8.4,],sources: [hvr-ghc]}}
15+ addons: {apt: {packages: [cabal-install-1.24,ghc-8.0.2],sources: [hvr-ghc]}}
11− - env: CABALVER=1.22 GHCVER=7.10.3
16+ - env: CABALVER=2.2 GHCVER=8.2.2
12− addons: {apt: {packages: [cabal-install-1.22,ghc-7.10.3],sources: [hvr-ghc]}}
17+ addons: {apt: {packages: [cabal-install-2.2,ghc-8.2.2], sources: [hvr-ghc]}}
13− - env: CABALVER=1.24 GHCVER=8.0.1
18+ - env: CABALVER=2.2 GHCVER=8.4.3
14− addons: {apt: {packages: [cabal-install-1.24,ghc-8.0.1],sources: [hvr-ghc]}}
19+ addons: {apt: {packages: [cabal-install-2.2,ghc-8.4.3], sources: [hvr-ghc]}}
15− - env: CABALVER=head GHCVER=head
16− addons: {apt: {packages: [cabal-install-head,ghc-head], sources: [hvr-ghc]}}
17−
18− allow_failures:
19− - env: CABALVER=head GHCVER=head
20
20
21before_install:
21before_install:
22 - export PATH=/opt/ghc/$GHCVER/bin:/opt/cabal/$CABALVER/bin:$PATH
22 - export PATH=/opt/ghc/$GHCVER/bin:/opt/cabal/$CABALVER/bin:$PATH
@@ -30,23 +30,14 @@ install:
30script:
30script:
31 - case "$CABALVER" in
31 - case "$CABALVER" in
32 "1.16") cabal configure -v2 -f dev ;;
32 "1.16") cabal configure -v2 -f dev ;;
33− "1.18") cabal configure --enable-tests --enable-library-coverage -v2 -f dev ;;
34− *) cabal configure --enable-tests --enable-coverage -v2 -f dev ;;
33+ *) cabal configure --enable-tests -v2 -f dev ;;
35 esac
34 esac
36 - cabal build
35 - cabal build
36+ - cabal sdist
37 - case "$GHCVER" in
37 - case "$GHCVER" in
38− "7.6.3") true ;;
38+ "8.4.3") cabal haddock | grep "100%" | wc -l | grep "1" ;;
39− *) cabal test --show-details=always ;;
39+ *) cabal haddock | grep "100%" | wc -l | grep "13" ;;
40 esac
40 esac
41− - cabal sdist
42− - cabal haddock | grep "100%" | wc -l | grep "13"
43−
44−# after_script:
45−# - cabal install hpc-coveralls
46−# - export PATH=~/.cabal/bin:$PATH
47−# - hpc-coveralls --coverage-mode=StrictlyFullLines
48−# --exclude-dir=tests
49−# test
50
41
51notifications:
42notifications:
52 email: false
43 email: false
CHANGELOG.md+23
@@ -1,3 +1,26 @@
1+## Inflections 0.4.0.6
2+* Support `megaparsec` == 9.0
3+
4+## Inflections 0.4.0.5
5+* Support `megaparsec` == 8.0
6+
7+## Inflections 0.4.0.3
8+* Support `megaparsec` == 7.0*.
9+* **Drop support** for `GHC 7.8.4`.
10+
11+## Inflections 0.4.0.3
12+* Support `exceptions` == 0.10.*
13+
14+## Inflections 0.4.0.2
15+* Support `exceptions` == 0.9.*
16+
17+## Inflections 0.4.0.1
18+* Support `megaparsec` >= 6.4.0
19+
20+## Inflections 0.4.0.0
21+
22+* Update megaparsec to version 6.
23+
1## Inflections 0.3.0.0
24## Inflections 0.3.0.0
2
25
3* A more type-safe API forbidding creation of invalid words.
26* A more type-safe API forbidding creation of invalid words.
LICENSE+17−18
@@ -1,22 +1,21 @@
1−Copyright (c) 2014-2016 Justin Leitgeb
2−
3MIT License
1MIT License
4
2
3+Copyright (c) 2022 Stack Builders Inc.
4+
5−Permission is hereby granted, free of charge, to any person obtaining
5+Permission is hereby granted, free of charge, to any person obtaining a copy
6−a copy of this software and associated documentation files (the
6+of this software and associated documentation files (the "Software"), to deal
7−"Software"), to deal in the Software without restriction, including
7+in the Software without restriction, including without limitation the rights
8−without limitation the rights to use, copy, modify, merge, publish,
9−distribute, sublicense, and/or sell copies of the Software, and to
8+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10−permit persons to whom the Software is furnished to do so, subject to
9+copies of the Software, and to permit persons to whom the Software is
11−the following conditions:
10+furnished to do so, subject to the following conditions:
12
11
13−The above copyright notice and this permission notice shall be
12+The above copyright notice and this permission notice shall be included in all
14−included in all copies or substantial portions of the Software.
13+copies or substantial portions of the Software.
15
14
16−THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17−EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18−MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19−NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20−LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
18+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21−OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
19+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22−WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+SOFTWARE.
README.md+11−8
@@ -49,11 +49,10 @@ If you want to specify a custom default replacement or approximation table for
49the `transliterate` function, you should instead call the `transliterateCustom`
49the `transliterate` function, you should instead call the `transliterateCustom`
50function which accepts a String for replacements and a Map for substitution.
50function which accepts a String for replacements and a Map for substitution.
51
51
52−## TODO
52+## Future work
53
53
54−I'd like this library to implement other functions found in the Rails
54+Ideally, we want to implement other functions found in the Rails
55−inflections library. If you need one of those functions, please submit a pull
55+inflections library. If you need one of those functions, please submit a pull request!
56−request!
57
56
58## Further documentation
57## Further documentation
59
58
@@ -64,10 +63,14 @@ For more information, please see the the
64
63
65Justin Leitgeb <justin@stackbuilders.com>
64Justin Leitgeb <justin@stackbuilders.com>
66
65
67−## Contributing
66+## License
68
67
69−You may submit pull requests to this repository on GitHub. Tests are appreciated with your contribution.
68+MIT, see [the LICENSE file](LICENSE).
70
69
71−## License
70+## Contributing
71+
72+Do you want to contribute to this project? Please take a look at our [contributing guideline](/docs/CONTRIBUTING.md) to know how you can help us build it.
72
73
73−MIT - see [the LICENSE file](LICENSE).
74+---
75+<img src="https://cdn.stackbuilders.com/media/images/Sb-supports.original.png" alt="Stack Builders" width="50%"></img>
76+[Check out our libraries](https://github.com/stackbuilders/) | [Join our team](https://www.stackbuilders.com/join-us/)
Text/Inflections.hs+34−12
@@ -96,6 +96,7 @@ module Text.Inflections
96 , camelizeCustom
96 , camelizeCustom
97 , dasherize
97 , dasherize
98 , humanize
98 , humanize
99+ , humanizeCustom
99 , underscore
100 , underscore
100 , titleize
101 , titleize
101 , Transliterations
102 , Transliterations
@@ -110,15 +111,17 @@ module Text.Inflections
110 , toUnderscore
111 , toUnderscore
111 , toDashed
112 , toDashed
112 , toCamelCased
113 , toCamelCased
114+ , toHumanized
113 , betterThrow )
115 , betterThrow )
114where
116where
115
117
116import Control.Monad.Catch (MonadThrow (..))
118import Control.Monad.Catch (MonadThrow (..))
117import Data.Text (Text)
119import Data.Text (Text)
120+import Data.Void (Void)
118import Text.Inflections.Camelize (camelize, camelizeCustom)
121import Text.Inflections.Camelize (camelize, camelizeCustom)
119import Text.Inflections.Dasherize (dasherize)
122import Text.Inflections.Dasherize (dasherize)
120import Text.Inflections.Data (Transliterations, defaultTransliterations)
123import Text.Inflections.Data (Transliterations, defaultTransliterations)
121−import Text.Inflections.Humanize (humanize)
124+import Text.Inflections.Humanize (humanize, humanizeCustom)
122import Text.Inflections.Ordinal (ordinal, ordinalize)
125import Text.Inflections.Ordinal (ordinal, ordinalize)
123import Text.Inflections.Parameterize (parameterize, parameterizeCustom)
126import Text.Inflections.Parameterize (parameterize, parameterizeCustom)
124import Text.Inflections.Parse.CamelCase (parseCamelCase)
127import Text.Inflections.Parse.CamelCase (parseCamelCase)
@@ -139,7 +142,7 @@ import Prelude hiding (Word)
139--
142--
140-- >>> toUnderscore "FooBarBazz"
143-- >>> toUnderscore "FooBarBazz"
141-- "foo_bar_bazz"
144-- "foo_bar_bazz"
142−toUnderscore :: Text -> Either (ParseError Char Dec) Text
145+toUnderscore :: Text -> Either (ParseErrorBundle Text Void) Text
143toUnderscore = fmap underscore . parseCamelCase []
146toUnderscore = fmap underscore . parseCamelCase []
144
147
145-- | Transforms CamelCasedString to snake-cased-string-with-dashes.
148-- | Transforms CamelCasedString to snake-cased-string-with-dashes.
@@ -148,26 +151,45 @@ toUnderscore = fmap underscore . parseCamelCase []
148--
151--
149-- >>> toDashed "FooBarBazz"
152-- >>> toDashed "FooBarBazz"
150-- "foo-bar-bazz"
153-- "foo-bar-bazz"
151−toDashed :: Text -> Either (ParseError Char Dec) Text
154+toDashed :: Text -> Either (ParseErrorBundle Text Void) Text
152toDashed = fmap dasherize . parseCamelCase []
155toDashed = fmap dasherize . parseCamelCase []
153
156
154-- | Transforms underscored_text to CamelCasedText. If first argument is
157-- | Transforms underscored_text to CamelCasedText. If first argument is
155−-- 'True' then FirstCharacter in result string will be in upper case. If
158+-- 'True' then the first character in the result string will be in upper case. If
156−-- 'False' then firstCharacter will be in lower case.
159+-- 'False' then the first character will be in lower case.
157--
160--
158−-- > toCamelCased t = fmap (camelizeCustom t) . parseSnakeCase []
161+-- > toCamelCased c = fmap (camelizeCustom c) . parseSnakeCase []
159--
162--
160-- >>> toCamelCased True "foo_bar_bazz"
163-- >>> toCamelCased True "foo_bar_bazz"
161-- "FooBarBazz"
164-- "FooBarBazz"
162-- >>> toCamelCased False "foo_bar_bazz"
165-- >>> toCamelCased False "foo_bar_bazz"
163-- "fooBarBazz"
166-- "fooBarBazz"
164toCamelCased
167toCamelCased
165− :: Bool -- ^ Capitalize the first character
168+ :: Bool -- ^ Capitalize the first character
166− -> Text -- ^ Input
169+ -> Text -- ^ Input
167− -> Either (ParseError Char Dec) Text -- ^ Ouput
170+ -> Either (ParseErrorBundle Text Void) Text -- ^ Output
168−toCamelCased t = fmap (camelizeCustom t) . parseSnakeCase []
171+toCamelCased c = fmap (camelizeCustom c) . parseSnakeCase []
169
172
173+-- | Transforms underscored_text to space-separated human-readable text.
174+-- If first argument is 'True' then the first character in the result
175+-- string will be in upper case. If 'False' then the first character will be
176+-- in lower case.
177+--
178+-- > toHumanized c = fmap (humanizeCustom c) . parseSnakeCase []
179+--
180+-- >>> toHumanized True "foo_bar_bazz"
181+-- "Foo bar bazz"
182+-- >>> toHumanized False "foo_bar_bazz"
183+-- "foo bar bazz"
184+--
185+-- /since 0.3.0.0/
186+toHumanized
187+ :: Bool -- ^ Capitalize the first character
188+ -> Text -- ^ Input
189+ -> Either (ParseErrorBundle Text Void) Text -- ^ Output
190+toHumanized c = fmap (humanizeCustom c) . parseSnakeCase []
191+
170−-- | Lift something of type @'Either' ('ParseError' 'Char' 'Dec') a@ to
192+-- | Lift something of type @'Either' ('ParseError' 'Char' 'Void') a@ to
171-- an instance of 'MonadThrow'. Useful when you want to shortcut on parsing
193-- an instance of 'MonadThrow'. Useful when you want to shortcut on parsing
172-- failures and you're in an instance of 'MonadThrow'.
194-- failures and you're in an instance of 'MonadThrow'.
173--
195--
@@ -175,6 +197,6 @@ toCamelCased t = fmap (camelizeCustom t) . parseSnakeCase []
175--
197--
176-- /since 0.3.0.0/
198-- /since 0.3.0.0/
177
199
178−betterThrow :: MonadThrow m => Either (ParseError Char Dec) a -> m a
200+betterThrow :: MonadThrow m => Either (ParseErrorBundle Text Void) a -> m a
179betterThrow (Left err) = throwM (InflectionParsingFailed err)
201betterThrow (Left err) = throwM (InflectionParsingFailed err)
180betterThrow (Right x) = return x
202betterThrow (Right x) = return x
Text/Inflections/Humanize.hs+24−3
@@ -13,7 +13,8 @@
13{-# LANGUAGE OverloadedStrings #-}
13{-# LANGUAGE OverloadedStrings #-}
14
14
15module Text.Inflections.Humanize
15module Text.Inflections.Humanize
16− ( humanize )
16+ ( humanize
17+ , humanizeCustom )
17where
18where
18
19
19import Data.Text (Text)
20import Data.Text (Text)
@@ -36,7 +37,27 @@ import Control.Applicative
36humanize
37humanize
37 :: [SomeWord] -- ^ List of words, first of which will be capitalized
38 :: [SomeWord] -- ^ List of words, first of which will be capitalized
38 -> Text -- ^ The humanized output
39 -> Text -- ^ The humanized output
39−humanize xs' =
40+humanize = humanizeCustom True
41+
42+
43+-- | Separate words with spaces, optionally capitalizing the first word. Like
44+-- 'Text.Inflections.Titleize.titleize', this is meant for creating pretty
45+-- output.
46+--
47+-- >>> foo <- SomeWord <$> mkWord "foo"
48+-- >>> bar <- SomeWord <$> mkAcronym "bar"
49+-- >>> bazz <- SomeWord <$> mkWord "bazz"
50+-- >>> humanizeCustom True [foo,bar,bazz]
51+-- "Foo bar bazz"
52+-- >>> humanizeCustom False [foo,bar,bazz]
53+-- "foo bar bazz"
54+--
55+-- /since 0.3.0.0/
56+humanizeCustom
57+ :: Bool -- ^ Whether to capitalize the first character in the output String
58+ -> [SomeWord] -- ^ List of words, first of which will be capitalized
59+ -> Text -- ^ The humanized output
60+humanizeCustom c xs' =
40 case unSomeWord (T.replace "_" " ") <$> xs' of
61 case unSomeWord (T.replace "_" " ") <$> xs' of
41 [] -> ""
62 [] -> ""
42− (x:xs) -> T.unwords $ T.toTitle x : (T.toLower <$> xs)
63+ (x:xs) -> T.unwords $ (if c then T.toTitle else T.toLower) x : (T.toLower <$> xs)
Text/Inflections/Ordinal.hs−1
@@ -16,7 +16,6 @@ module Text.Inflections.Ordinal
16 , ordinal )
16 , ordinal )
17where
17where
18
18
19−import Data.Monoid ((<>))
20import Data.Text (Text)
19import Data.Text (Text)
21import qualified Data.Text as T
20import qualified Data.Text as T
22
21
Text/Inflections/Parse/CamelCase.hs+12−5
@@ -17,11 +17,16 @@ module Text.Inflections.Parse.CamelCase
17 ( parseCamelCase )
17 ( parseCamelCase )
18where
18where
19
19
20+# if MIN_VERSION_base(4,8,0)
20−import Control.Applicative
21+import Control.Applicative (empty, many, (<|>))
22+#else
23+import Control.Applicative (empty, many, (<|>), (<$>), (<*))
24+#endif
21import Data.Text (Text)
25import Data.Text (Text)
26+import Data.Void (Void)
22import Text.Inflections.Types
27import Text.Inflections.Types
23−import Text.Megaparsec
28+import Text.Megaparsec (Parsec, ParseErrorBundle, choice, eof, parse)
24−import Text.Megaparsec.Text
29+import Text.Megaparsec.Char
25import qualified Data.Text as T
30import qualified Data.Text as T
26
31
27#if MIN_VERSION_base(4,8,0)
32#if MIN_VERSION_base(4,8,0)
@@ -31,6 +36,8 @@ import Data.Foldable
31import Prelude hiding (elem)
36import Prelude hiding (elem)
32#endif
37#endif
33
38
39+type Parser = Parsec Void Text
40+
34-- | Parse a CamelCase string.
41-- | Parse a CamelCase string.
35--
42--
36-- >>> bar <- mkAcronym "bar"
43-- >>> bar <- mkAcronym "bar"
@@ -44,7 +51,7 @@ import Prelude hiding (elem)
44parseCamelCase :: (Foldable f, Functor f)
51parseCamelCase :: (Foldable f, Functor f)
45 => f (Word 'Acronym) -- ^ Collection of acronyms
52 => f (Word 'Acronym) -- ^ Collection of acronyms
46 -> Text -- ^ Input
53 -> Text -- ^ Input
47− -> Either (ParseError Char Dec) [SomeWord] -- ^ Result of parsing
54+ -> Either (ParseErrorBundle Text Void) [SomeWord] -- ^ Result of parsing
48parseCamelCase acronyms = parse (parser acronyms) ""
55parseCamelCase acronyms = parse (parser acronyms) ""
49
56
50parser :: (Foldable f, Functor f)
57parser :: (Foldable f, Functor f)
@@ -59,7 +66,7 @@ acronym :: (Foldable f, Functor f)
59 => f (Word 'Acronym)
66 => f (Word 'Acronym)
60 -> Parser (Word 'Acronym)
67 -> Parser (Word 'Acronym)
61acronym acronyms = do
68acronym acronyms = do
62− x <- T.pack <$> choice (string . T.unpack . unWord <$> acronyms)
69+ x <- choice (string . unWord <$> acronyms)
63 case mkAcronym x of
70 case mkAcronym x of
64 Nothing -> empty -- cannot happen if the system is sound
71 Nothing -> empty -- cannot happen if the system is sound
65 Just acr -> return acr
72 Just acr -> return acr
Text/Inflections/Parse/SnakeCase.hs+11−4
@@ -16,11 +16,16 @@ module Text.Inflections.Parse.SnakeCase
16 ( parseSnakeCase )
16 ( parseSnakeCase )
17where
17where
18
18
19+# if MIN_VERSION_base(4,8,0)
19−import Control.Applicative
20+import Control.Applicative (empty, some)
21+#else
22+import Control.Applicative (empty, some, (<$>), (<*))
23+#endif
20import Data.Text (Text)
24import Data.Text (Text)
25+import Data.Void (Void)
21import Text.Inflections.Types
26import Text.Inflections.Types
22−import Text.Megaparsec
27+import Text.Megaparsec (Parsec, ParseErrorBundle, eof, sepBy, parse)
23−import Text.Megaparsec.Text
28+import Text.Megaparsec.Char
24import qualified Data.Text as T
29import qualified Data.Text as T
25
30
26#if MIN_VERSION_base(4,8,0)
31#if MIN_VERSION_base(4,8,0)
@@ -30,6 +35,8 @@ import Data.Foldable
30import Prelude hiding (elem)
35import Prelude hiding (elem)
31#endif
36#endif
32
37
38+type Parser = Parsec Void Text
39+
33-- | Parse a snake_case string.
40-- | Parse a snake_case string.
34--
41--
35-- >>> bar <- mkAcronym "bar"
42-- >>> bar <- mkAcronym "bar"
@@ -43,7 +50,7 @@ import Prelude hiding (elem)
43parseSnakeCase :: (Foldable f, Functor f)
50parseSnakeCase :: (Foldable f, Functor f)
44 => f (Word 'Acronym) -- ^ Collection of acronyms
51 => f (Word 'Acronym) -- ^ Collection of acronyms
45 -> Text -- ^ Input
52 -> Text -- ^ Input
46− -> Either (ParseError Char Dec) [SomeWord] -- ^ Result of parsing
53+ -> Either (ParseErrorBundle Text Void) [SomeWord] -- ^ Result of parsing
47parseSnakeCase acronyms = parse (parser acronyms) ""
54parseSnakeCase acronyms = parse (parser acronyms) ""
48
55
49parser :: (Foldable f, Functor f)
56parser :: (Foldable f, Functor f)
Text/Inflections/Titleize.hs+2−2
@@ -17,7 +17,7 @@ import Data.Text (Text)
17import Text.Inflections.Types
17import Text.Inflections.Types
18import qualified Data.Text as T
18import qualified Data.Text as T
19
19
20−-- | Capitalize all the Words in the input list.
20+-- | Capitalize all the 'SomeWord' words in the input list.
21--
21--
22-- >>> foo <- SomeWord <$> mkWord "foo"
22-- >>> foo <- SomeWord <$> mkWord "foo"
23-- >>> bar <- SomeWord <$> mkAcronym "bar"
23-- >>> bar <- SomeWord <$> mkAcronym "bar"
@@ -25,6 +25,6 @@ import qualified Data.Text as T
25-- >>> titleize [foo,bar,bazz]
25-- >>> titleize [foo,bar,bazz]
26-- "Foo bar Bazz"
26-- "Foo bar Bazz"
27titleize
27titleize
28− :: [SomeWord] -- ^ List of words, first of which will be capitalized
28+ :: [SomeWord] -- ^ List of words, of which all 'SomeWord' words will be capitalized and all acronyms will be left as is
29 -> Text -- ^ The titleized 'Text'
29 -> Text -- ^ The titleized 'Text'
30titleize = T.unwords . fmap (unSomeWord T.toTitle)
30titleize = T.unwords . fmap (unSomeWord T.toTitle)
Text/Inflections/Types.hs+2−1
@@ -33,6 +33,7 @@ where
33import Control.Monad.Catch
33import Control.Monad.Catch
34import Data.Char (isAlphaNum)
34import Data.Char (isAlphaNum)
35import Data.Data (Data)
35import Data.Data (Data)
36+import Data.Void (Void)
36import Data.Text (Text)
37import Data.Text (Text)
37import Data.Typeable (Typeable)
38import Data.Typeable (Typeable)
38import GHC.Generics (Generic)
39import GHC.Generics (Generic)
@@ -135,7 +136,7 @@ instance Transformable (Word 'Acronym) where
135-- /since 0.3.0.0/
136-- /since 0.3.0.0/
136
137
137data InflectionException
138data InflectionException
138− = InflectionParsingFailed (ParseError Char Dec)
139+ = InflectionParsingFailed (ParseErrorBundle Text Void)
139 | InflectionInvalidWord Text
140 | InflectionInvalidWord Text
140 | InflectionInvalidAcronym Text
141 | InflectionInvalidAcronym Text
141 deriving (Eq, Show, Typeable, Data, Generic)
142 deriving (Eq, Show, Typeable, Data, Generic)
docs/CODE_OF_CONDUCT.mdnew file+35
@@ -0,0 +1,35 @@
1+# Code of conduct
2+
3+## Purpose
4+The primary goal of this Code of Conduct is to enable an open and welcoming environment. We pledge to making participation in our project a harassment-free experience for everyone, regardless of gender, sexual
5+orientation, ability, ethnicity, socioeconomic status, and religion (or lack thereof).
6+
7+## General recommendations
8+Examples of behavior that contributes to creating a positive environment include:
9+
10+- Using welcoming and inclusive language
11+- Being respectful of differing viewpoints and experiences
12+- Gracefully accepting constructive criticism
13+- Focusing on what is best for the community
14+- Showing empathy towards other community members
15+
16+Examples of unacceptable behavior by participants include:
17+
18+- The use of sexualized language or imagery and unwelcome sexual attention or advances
19+- Trolling, insulting/derogatory comments, and personal or political attacks
20+- Public or private harassment
21+- Publishing others' private information, such as a physical or electronic address, without explicit permission
22+- Other conduct which could reasonably be considered inappropriate in a professional setting
23+
24+## Maintainer responsibilities
25+Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
26+
27+Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
28+
29+## Scope
30+This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
31+
32+## Enforcement
33+Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at [community@stackbuilders.com](mailto:community@stackbuilders.com). All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
34+
35+Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
docs/CONTRIBUTING.mdnew file+13
@@ -0,0 +1,13 @@
1+
2+Thank you for your interest in contributing to this Stack Builders' library. To contribute, please take our [Code of Conduct](CODE_OF_CONDUCT.md) into account, along with the following recommendations:
3+
4+- When submitting contributions to this repository, please make sure to discuss with the maintainer(s) the change you want to make. You can do this through an issue, or by sending an email to [community@stackbuilders.com](mailto:community@stackbuilders.com)
5+
6+- Once the change has been discussed with the maintainer(s), feel free to open a Pull Request. Please include a link to the issue you're trying to solve, or a quick summary of the discussed changes.
7+
8+- If adding any new features that you think should be considered in the README file, please add that information in your Pull Request.
9+
10+- Once you get an approval from any of the maintainers, please merge your Pull Request. Keep in mind that some of our Stack Builders repositories use CI/CD pipelines, so you will need to pass all of the required checks before merging.
11+
12+## Getting help
13+Contact any of our current maintainers, or send us an email at [community@stackbuilders.com](mailto:community@stackbuilders.com) for more information. Thank you for contributing!
inflections.cabal+20−10
@@ -1,5 +1,5 @@
1name: inflections
1name: inflections
2−version: 0.3.0.0
2+version: 0.4.0.6
3synopsis: Inflections library for Haskell
3synopsis: Inflections library for Haskell
4description:
4description:
5 Inflections provides methods for singularization, pluralization,
5 Inflections provides methods for singularization, pluralization,
@@ -48,10 +48,13 @@ library
48 else
48 else
49 ghc-options: -O2 -Wall
49 ghc-options: -O2 -Wall
50 build-depends: base >= 4.6 && < 5.0
50 build-depends: base >= 4.6 && < 5.0
51− , exceptions >= 0.6 && < 0.9
51+ , exceptions
52− , megaparsec >= 5.0 && < 6.0
52+ , megaparsec
53− , text >= 0.2 && < 1.3
53+ , text
54− , unordered-containers >= 0.2.7 && < 0.3
54+ , unordered-containers
55+ if !impl(ghc >= 7.10)
56+ build-depends: void == 0.7.*
57+
55 default-language: Haskell2010
58 default-language: Haskell2010
56
59
57test-suite test
60test-suite test
@@ -59,12 +62,19 @@ test-suite test
59 hs-source-dirs: test
62 hs-source-dirs: test
60 main-is: Spec.hs
63 main-is: Spec.hs
61 build-depends: inflections
64 build-depends: inflections
62− , QuickCheck >= 2.7.6 && < 3.0
65+ , QuickCheck
63 , base >= 4.6 && < 5.0
66 , base >= 4.6 && < 5.0
67+ , containers
64− , hspec >= 2.0 && < 3.0
68+ , hspec
65− , hspec-megaparsec >= 0.3 && < 0.4
69+ , hspec-megaparsec
66− , megaparsec >= 5.1 && < 6.0
70+ , megaparsec
67− , text >= 0.2 && < 1.3
71+ , text
72+ if !impl(ghc >= 7.10)
73+ build-depends: void == 0.7.*
74+
75+ if !impl(ghc >= 8.0)
76+ build-depends: semigroups == 0.18.*
77+
68 if flag(dev)
78 if flag(dev)
69 ghc-options: -Wall -Werror
79 ghc-options: -Wall -Werror
70 else
80 else
stack.yaml+1−6
@@ -1,6 +1 @@
1−resolver: lts-7.13
1+resolver: lts-21.3
2−packages:
3−- '.'
4−extra-deps:
5−- hspec-megaparsec-0.3.0
6−- megaparsec-5.1.2
stack.yaml.locknew file+12
@@ -0,0 +1,12 @@
1+# This file was autogenerated by Stack.
2+# You should not edit this file by hand.
3+# For more information, please see the documentation at:
4+# https://docs.haskellstack.org/en/stable/lock_files
5+
6+packages: []
7+snapshots:
8+- completed:
9+ sha256: 97710f56bf093fca0ee5d8dbe19d96b654c752e405b66795b4baedd84a794c60
10+ size: 640010
11+ url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/21/3.yaml
12+ original: lts-21.3
test/Text/Inflections/HumanizeSpec.hs+40−13
@@ -12,16 +12,43 @@ import Control.Applicative
12#endif
12#endif
13
13
14spec :: Spec
14spec :: Spec
15+spec = do
15−spec = describe "humazine" $ do
16+ describe "humanize" $ do
16− it "converts snake case to a human-readable string" $ do
17+ it "converts snake case to a human-readable string" $ do
17− employee <- SomeWord <$> mkWord "employee"
18+ employee <- SomeWord <$> mkWord "employee"
18− salary <- SomeWord <$> mkWord "salary"
19+ salary <- SomeWord <$> mkWord "salary"
19− humanize [employee,salary] `shouldBe` "Employee salary"
20+ humanize [employee,salary] `shouldBe` "Employee salary"
20− it "turns underscores into spaces" $ do
21+ it "turns underscores into spaces" $ do
21− employee <- SomeWord <$> mkWord "employee"
22+ employee <- SomeWord <$> mkWord "employee"
22− has <- SomeWord <$> mkWord "has"
23+ has <- SomeWord <$> mkWord "has"
23− salary <- SomeWord <$> mkWord "salary"
24+ salary <- SomeWord <$> mkWord "salary"
24− humanize [employee, has, salary] `shouldBe` "Employee has salary"
25+ humanize [employee, has, salary] `shouldBe` "Employee has salary"
25− it "capitalizes the first word of a sentence" $ do
26+ it "capitalizes the first word of a sentence" $ do
26− underground <- SomeWord <$> mkWord "underground"
27+ underground <- SomeWord <$> mkWord "underground"
28+ humanize [underground] `shouldBe` "Underground"
29+ describe "humanizeCustom False" $ do
30+ it "converts snake case to a human-readable string" $ do
31+ employee <- SomeWord <$> mkWord "employee"
32+ salary <- SomeWord <$> mkWord "salary"
33+ humanizeCustom False [employee,salary] `shouldBe` "employee salary"
34+ it "turns underscores into spaces" $ do
35+ employee <- SomeWord <$> mkWord "employee"
36+ has <- SomeWord <$> mkWord "has"
37+ salary <- SomeWord <$> mkWord "salary"
38+ humanizeCustom False [employee, has, salary] `shouldBe` "employee has salary"
39+ it "lower-cases the first word of a sentence" $ do
40+ underground <- SomeWord <$> mkWord "underground"
41+ humanizeCustom False [underground] `shouldBe` "underground"
42+ describe "humanizeCustom True" $ do
43+ it "converts snake case to a human-readable string" $ do
44+ employee <- SomeWord <$> mkWord "employee"
45+ salary <- SomeWord <$> mkWord "salary"
46+ humanizeCustom True [employee,salary] `shouldBe` "Employee salary"
47+ it "turns underscores into spaces" $ do
48+ employee <- SomeWord <$> mkWord "employee"
49+ has <- SomeWord <$> mkWord "has"
50+ salary <- SomeWord <$> mkWord "salary"
51+ humanizeCustom True [employee, has, salary] `shouldBe` "Employee has salary"
52+ it "capitalizes the first word of a sentence" $ do
53+ underground <- SomeWord <$> mkWord "underground"
27− humanize [underground] `shouldBe` "Underground"
54+ humanizeCustom True [underground] `shouldBe` "Underground"
test/Text/InflectionsSpec.hs+60−1
@@ -1,10 +1,60 @@
1+{-# OPTIONS_GHC -fno-warn-orphans #-}
2+{-# LANGUAGE CPP #-}
1{-# LANGUAGE OverloadedStrings #-}
3{-# LANGUAGE OverloadedStrings #-}
2
4
3module Text.InflectionsSpec (spec) where
5module Text.InflectionsSpec (spec) where
4
6
7+import Data.Void
5import Test.Hspec
8import Test.Hspec
6import Test.QuickCheck
9import Test.QuickCheck
7import Text.Inflections
10import Text.Inflections
11+import Text.Megaparsec
12+import Data.Text
13+
14+import qualified Data.List.NonEmpty as NE
15+import qualified Data.Set as S
16+
17+#if !MIN_VERSION_base(4,8,0)
18+import Control.Applicative
19+#endif
20+
21+instance Arbitrary Text where
22+ arbitrary = pack <$> (arbitrary :: Gen String)
23+
24+arbitraryParseErrorBundle :: Gen (ParseErrorBundle Text Void)
25+arbitraryParseErrorBundle = ParseErrorBundle <$> nonEmptyParseErrors <*> arbitraryPosState
26+ where
27+ posArbitrary = mkPos <$> ((+1) . abs <$> arbitrary)
28+ nonEmptyParseErrors :: Gen (NE.NonEmpty (ParseError Text Void))
29+ nonEmptyParseErrors = NE.fromList <$> listOf1 arbitraryParseError
30+ arbitrarySourcePos :: Gen SourcePos
31+ arbitrarySourcePos = SourcePos <$> arbitrary <*> posArbitrary <*> posArbitrary
32+ arbitraryPosState :: Gen (PosState Text)
33+ arbitraryPosState =
34+ PosState
35+ <$> arbitrary
36+ <*> arbitrary
37+ <*> arbitrarySourcePos
38+ <*> posArbitrary
39+ <*> arbitrary
40+
41+arbitraryParseError :: Gen (ParseError Text Void)
42+arbitraryParseError = oneof [trivialError, fancyError]
43+ where
44+ trivialError = TrivialError <$> arbitrary <*> maybeErrorItem <*> setErrorItem
45+ fancyError = FancyError <$> arbitrary <*> setErrorFancy
46+ setErrorFancy = S.fromList <$> listOf arbitraryErrorFancy
47+ maybeErrorItem = oneof [ Just <$> arbitraryErrorItem, return Nothing]
48+ setErrorItem = S.fromList <$> listOf arbitraryErrorItem
49+
50+arbitraryErrorFancy :: Gen (ErrorFancy e)
51+arbitraryErrorFancy = oneof [ ErrorFail <$> arbitrary ]
52+
53+arbitraryErrorItem :: (Arbitrary e) => Gen (ErrorItem e)
54+arbitraryErrorItem = oneof [ tokens_, labels_, return EndOfInput ]
55+ where
56+ tokens_ = Tokens <$> (NE.fromList <$> listOf1 arbitrary)
57+ labels_ = Label <$> (NE.fromList <$> listOf1 arbitrary)
8
58
9spec :: Spec
59spec :: Spec
10spec = do
60spec = do
@@ -27,12 +77,21 @@ spec = do
27 it "converts snake case to camel case with the first word capitalized" $
77 it "converts snake case to camel case with the first word capitalized" $
28 toCamelCased True "underscored_text" `shouldBe` Right "UnderscoredText"
78 toCamelCased True "underscored_text" `shouldBe` Right "UnderscoredText"
29
79
80+ describe "toHumanized" $ do
81+ context "when the first argument is False" $
82+ it "converts snake case to human-readable form with lower-case initial letter" $
83+ toHumanized False "underscored_text" `shouldBe` Right "underscored text"
84+ context "when the first argument is True" $
85+ it "converts snake case to human-readable form with the first word capitalized" $
86+ toHumanized True "underscored_text" `shouldBe` Right "Underscored text"
87+
30 describe "betterThrow" $ do
88 describe "betterThrow" $ do
31 context "when given a parse error" $
89 context "when given a parse error" $
32 it "throws the correct exception" $
90 it "throws the correct exception" $
33− property $ \err ->
91+ property $ forAll arbitraryParseErrorBundle $ \err ->
34 betterThrow (Left err) `shouldThrow`
92 betterThrow (Left err) `shouldThrow`
35 (== InflectionParsingFailed err)
93 (== InflectionParsingFailed err)
94+
36 context "when given a value in Right" $
95 context "when given a value in Right" $
37 it "returns the value" $
96 it "returns the value" $
38 property $ \x ->
97 property $ \x ->