git.iko.soy/typeable/inflections-hs
on 2a2d028 help sign in
.travis.yml+26−17
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−
10matrix:
5matrix:
11 include:
6 include:
12− - env: CABALVER=1.24 GHCVER=7.10.3
7+ - env: CABALVER=1.16 GHCVER=7.6.3
13− addons: {apt: {packages: [cabal-install-1.24,ghc-7.10.3],sources: [hvr-ghc]}}
8+ addons: {apt: {packages: [cabal-install-1.16,ghc-7.6.3], sources: [hvr-ghc]}}
14− - env: CABALVER=1.24 GHCVER=8.0.2
9+ - env: CABALVER=1.18 GHCVER=7.8.4
15− addons: {apt: {packages: [cabal-install-1.24,ghc-8.0.2],sources: [hvr-ghc]}}
10+ addons: {apt: {packages: [cabal-install-1.18,ghc-7.8.4,],sources: [hvr-ghc]}}
16− - env: CABALVER=2.2 GHCVER=8.2.2
11+ - env: CABALVER=1.22 GHCVER=7.10.3
17− addons: {apt: {packages: [cabal-install-2.2,ghc-8.2.2], sources: [hvr-ghc]}}
12+ addons: {apt: {packages: [cabal-install-1.22,ghc-7.10.3],sources: [hvr-ghc]}}
18− - env: CABALVER=2.2 GHCVER=8.4.3
13+ - env: CABALVER=1.24 GHCVER=8.0.1
19− addons: {apt: {packages: [cabal-install-2.2,ghc-8.4.3], sources: [hvr-ghc]}}
14+ addons: {apt: {packages: [cabal-install-1.24,ghc-8.0.1],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,14 +30,23 @@ 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 ;;
33− *) cabal configure --enable-tests -v2 -f dev ;;
34+ *) cabal configure --enable-tests --enable-coverage -v2 -f dev ;;
34 esac
35 esac
35 - cabal build
36 - cabal build
36− - cabal sdist
37 - case "$GHCVER" in
37 - case "$GHCVER" in
38− "8.4.3") cabal haddock | grep "100%" | wc -l | grep "1" ;;
38+ "7.6.3") true ;;
39− *) cabal haddock | grep "100%" | wc -l | grep "13" ;;
39+ *) cabal test --show-details=always ;;
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
41
50
42notifications:
51notifications:
43 email: false
52 email: false
CHANGELOG.md−33
@@ -1,26 +1,3 @@
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−
24## Inflections 0.3.0.0
1## Inflections 0.3.0.0
25
2
26* A more type-safe API forbidding creation of invalid words.
3* A more type-safe API forbidding creation of invalid words.
@@ -32,16 +9,6 @@
32
9
33* Renamed `defaultMap` to `defaultTransliterations`.
10* Renamed `defaultMap` to `defaultTransliterations`.
34
11
35−* Words now can contain digits (recognized by all parsers).
36−
37−* `parseSnakeCase` now is not confused when a word happens to have prefix
38− coinciding with an acronym. This is harder to fix for `parseCamelCase`
39− because acronym may contain capital letters, so old behavior is preserved
40− for `parseCamelCase` for now.
41−
42−* `parseCamelCase` and `parseSnakeCase` take any instance of `Foldable` as a
43− collection of acronyms, not just lists.
44−
45* Added the `CHANGELOG.md` file.
12* Added the `CHANGELOG.md` file.
46
13
47* Switched test suite to Hspec.
14* Switched test suite to Hspec.
LICENSE+18−17
@@ -1,21 +1,22 @@
1−MIT License
1+Copyright (c) 2014-2016 Justin Leitgeb
2
2
3−Copyright (c) 2022 Stack Builders Inc.
3+MIT License
4
4
5−Permission is hereby granted, free of charge, to any person obtaining a copy
5+Permission is hereby granted, free of charge, to any person obtaining
6−of this software and associated documentation files (the "Software"), to deal
6+a copy of this software and associated documentation files (the
7−in the Software without restriction, including without limitation the rights
7+"Software"), to deal in the Software without restriction, including
8+without limitation the rights to use, copy, modify, merge, publish,
8−to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+distribute, sublicense, and/or sell copies of the Software, and to
9−copies of the Software, and to permit persons to whom the Software is
10+permit persons to whom the Software is furnished to do so, subject to
10−furnished to do so, subject to the following conditions:
11+the following conditions:
11
12
12−The above copyright notice and this permission notice shall be included in all
13+The above copyright notice and this permission notice shall be
13−copies or substantial portions of the Software.
14+included in all copies or substantial portions of the Software.
14
15
15−THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16−IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17−FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18−AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19−LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20−OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21−SOFTWARE.
README.md+8−11
@@ -49,10 +49,11 @@ 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−## Future work
52+## TODO
53
53
54−Ideally, we want to implement other functions found in the Rails
54+I'd like this library to implement other functions found in the Rails
55−inflections library. If you need one of those functions, please submit a pull request!
55+inflections library. If you need one of those functions, please submit a pull
56+request!
56
57
57## Further documentation
58## Further documentation
58
59
@@ -63,14 +64,10 @@ For more information, please see the the
63
64
64Justin Leitgeb <justin@stackbuilders.com>
65Justin Leitgeb <justin@stackbuilders.com>
65
66
66−## License
67−
68−MIT, see [the LICENSE file](LICENSE).
69−
70## Contributing
67## Contributing
71
68
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.
69+You may submit pull requests to this repository on GitHub. Tests are appreciated with your contribution.
70+
71+## License
73
72
74−---
73+MIT - see [the LICENSE file](LICENSE).
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+28−50
@@ -1,6 +1,6 @@
1-- |
1-- |
2-- Module : Text.Inflections
2-- Module : Text.Inflections
3−-- Description : Rails-like inflections library for common Text transformations.
3+-- Description : Rails-like inflections library for common String transformations.
4-- Copyright : (c) Justin Leitgeb
4-- Copyright : (c) Justin Leitgeb
5-- License : MIT
5-- License : MIT
6--
6--
@@ -8,7 +8,7 @@
8-- Stability : unstable
8-- Stability : unstable
9-- Portability : portable
9-- Portability : portable
10--
10--
11−-- This module provides methods for common 'Text' transformations, similar
11+-- This module provides methods for common String transformations, similar
12-- to the Inflections library found in Rails:
12-- to the Inflections library found in Rails:
13--
13--
14-- <http://api.rubyonrails.org/classes/ActiveSupport/Inflector.html>
14-- <http://api.rubyonrails.org/classes/ActiveSupport/Inflector.html>
@@ -17,7 +17,7 @@
17-- implementations in Rails' ActiveSupport, the philosophy of this library
17-- implementations in Rails' ActiveSupport, the philosophy of this library
18-- is fundamentally different. Where Rails tries to be as permissive as
18-- is fundamentally different. Where Rails tries to be as permissive as
19-- possible, and return a String when given any input, this library tries to
19-- possible, and return a String when given any input, this library tries to
20−-- output 'Text' that makes sense according to the function that is called.
20+-- output strings that make sense according to the function that is called.
21--
21--
22-- When you look closely at many of the functions in Rails' inflections
22-- When you look closely at many of the functions in Rails' inflections
23-- library, you will notice that many of them are partial. That is, they
23-- library, you will notice that many of them are partial. That is, they
@@ -33,7 +33,7 @@
33-- >>> "foo bar".underscore
33-- >>> "foo bar".underscore
34-- "foo bar"
34-- "foo bar"
35--
35--
36−-- The output isn't underscored — it contains a space! It turns out that
36+-- The output isn't underscored - it contains a space! It turns out that
37-- some of the functions from Inflections in ActiveSupport are /partial/.
37-- some of the functions from Inflections in ActiveSupport are /partial/.
38-- I.e., the outputs are really only specified for a certain range of the
38-- I.e., the outputs are really only specified for a certain range of the
39-- inputs allowed by the String type.
39-- inputs allowed by the String type.
@@ -42,7 +42,7 @@
42-- results by separating the parsing of strings into tokens from the
42-- results by separating the parsing of strings into tokens from the
43-- application of transformations. Let's see an example.
43-- application of transformations. Let's see an example.
44--
44--
45−-- First, we tokenize an underscored 'Text' using 'parseSnakeCase':
45+-- First, we tokenize an underscored String using 'parseSnakeCase':
46--
46--
47-- >>> parseSnakeCase [] "foo_bar"
47-- >>> parseSnakeCase [] "foo_bar"
48-- Right [Word "foo",Word "bar"]
48-- Right [Word "foo",Word "bar"]
@@ -79,24 +79,14 @@
79{-# LANGUAGE CPP #-}
79{-# LANGUAGE CPP #-}
80
80
81module Text.Inflections
81module Text.Inflections
82− ( -- * Types and helpers
83− Word
84− , WordType (..)
85− , mkWord
86− , mkAcronym
87− , unWord
88− , SomeWord (..)
89− , unSomeWord
90− , InflectionException (..)
91− -- * Parsing
82+ ( -- * Parsing
92− , parseSnakeCase
83+ parseSnakeCase
93 , parseCamelCase
84 , parseCamelCase
94 -- * Rendering
85 -- * Rendering
95 , camelize
86 , camelize
96 , camelizeCustom
87 , camelizeCustom
97 , dasherize
88 , dasherize
98 , humanize
89 , humanize
99− , humanizeCustom
100 , underscore
90 , underscore
101 , titleize
91 , titleize
102 , Transliterations
92 , Transliterations
@@ -107,21 +97,28 @@ module Text.Inflections
107 , transliterateCustom
97 , transliterateCustom
108 , ordinalize
98 , ordinalize
109 , ordinal
99 , ordinal
100+ -- * Types and helpers
101+ , Word
102+ , WordType (..)
103+ , mkWord
104+ , mkAcronym
105+ , unWord
106+ , SomeWord (..)
107+ , unSomeWord
108+ , InflectionException (..)
110 -- * Often used combinators
109 -- * Often used combinators
111 , toUnderscore
110 , toUnderscore
112 , toDashed
111 , toDashed
113 , toCamelCased
112 , toCamelCased
114− , toHumanized
115 , betterThrow )
113 , betterThrow )
116where
114where
117
115
118import Control.Monad.Catch (MonadThrow (..))
116import Control.Monad.Catch (MonadThrow (..))
119import Data.Text (Text)
117import Data.Text (Text)
120−import Data.Void (Void)
121import Text.Inflections.Camelize (camelize, camelizeCustom)
118import Text.Inflections.Camelize (camelize, camelizeCustom)
122import Text.Inflections.Dasherize (dasherize)
119import Text.Inflections.Dasherize (dasherize)
123import Text.Inflections.Data (Transliterations, defaultTransliterations)
120import Text.Inflections.Data (Transliterations, defaultTransliterations)
124−import Text.Inflections.Humanize (humanize, humanizeCustom)
121+import Text.Inflections.Humanize (humanize)
125import Text.Inflections.Ordinal (ordinal, ordinalize)
122import Text.Inflections.Ordinal (ordinal, ordinalize)
126import Text.Inflections.Parameterize (parameterize, parameterizeCustom)
123import Text.Inflections.Parameterize (parameterize, parameterizeCustom)
127import Text.Inflections.Parse.CamelCase (parseCamelCase)
124import Text.Inflections.Parse.CamelCase (parseCamelCase)
@@ -142,7 +139,7 @@ import Prelude hiding (Word)
142--
139--
143-- >>> toUnderscore "FooBarBazz"
140-- >>> toUnderscore "FooBarBazz"
144-- "foo_bar_bazz"
141-- "foo_bar_bazz"
145−toUnderscore :: Text -> Either (ParseErrorBundle Text Void) Text
142+toUnderscore :: Text -> Either (ParseError Char Dec) Text
146toUnderscore = fmap underscore . parseCamelCase []
143toUnderscore = fmap underscore . parseCamelCase []
147
144
148-- | Transforms CamelCasedString to snake-cased-string-with-dashes.
145-- | Transforms CamelCasedString to snake-cased-string-with-dashes.
@@ -151,45 +148,26 @@ toUnderscore = fmap underscore . parseCamelCase []
151--
148--
152-- >>> toDashed "FooBarBazz"
149-- >>> toDashed "FooBarBazz"
153-- "foo-bar-bazz"
150-- "foo-bar-bazz"
154−toDashed :: Text -> Either (ParseErrorBundle Text Void) Text
151+toDashed :: Text -> Either (ParseError Char Dec) Text
155toDashed = fmap dasherize . parseCamelCase []
152toDashed = fmap dasherize . parseCamelCase []
156
153
157-- | Transforms underscored_text to CamelCasedText. If first argument is
154-- | Transforms underscored_text to CamelCasedText. If first argument is
158−-- 'True' then the first character in the result string will be in upper case. If
155+-- 'True' then FirstCharacter in result string will be in upper case. If
159−-- 'False' then the first character will be in lower case.
156+-- 'False' then firstCharacter will be in lower case.
160--
157--
161−-- > toCamelCased c = fmap (camelizeCustom c) . parseSnakeCase []
158+-- > toCamelCased t = fmap (camelizeCustom t) . parseSnakeCase []
162--
159--
163-- >>> toCamelCased True "foo_bar_bazz"
160-- >>> toCamelCased True "foo_bar_bazz"
164-- "FooBarBazz"
161-- "FooBarBazz"
165-- >>> toCamelCased False "foo_bar_bazz"
162-- >>> toCamelCased False "foo_bar_bazz"
166-- "fooBarBazz"
163-- "fooBarBazz"
167toCamelCased
164toCamelCased
168− :: Bool -- ^ Capitalize the first character
165+ :: Bool -- ^ Capitalize the first character
169− -> Text -- ^ Input
166+ -> Text -- ^ Input
170− -> Either (ParseErrorBundle Text Void) Text -- ^ Output
167+ -> Either (ParseError Char Dec) Text -- ^ Ouput
171−toCamelCased c = fmap (camelizeCustom c) . parseSnakeCase []
168+toCamelCased t = fmap (camelizeCustom t) . parseSnakeCase []
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
169
192−-- | Lift something of type @'Either' ('ParseError' 'Char' 'Void') a@ to
170+-- | Lift something of type @'Either' ('ParseError' 'Char' 'Dec') a@ to
193-- an instance of 'MonadThrow'. Useful when you want to shortcut on parsing
171-- an instance of 'MonadThrow'. Useful when you want to shortcut on parsing
194-- failures and you're in an instance of 'MonadThrow'.
172-- failures and you're in an instance of 'MonadThrow'.
195--
173--
@@ -197,6 +175,6 @@ toHumanized c = fmap (humanizeCustom c) . parseSnakeCase []
197--
175--
198-- /since 0.3.0.0/
176-- /since 0.3.0.0/
199
177
200−betterThrow :: MonadThrow m => Either (ParseErrorBundle Text Void) a -> m a
178+betterThrow :: MonadThrow m => Either (ParseError Char Dec) a -> m a
201betterThrow (Left err) = throwM (InflectionParsingFailed err)
179betterThrow (Left err) = throwM (InflectionParsingFailed err)
202betterThrow (Right x) = return x
180betterThrow (Right x) = return x
Text/Inflections/Camelize.hs+2−2
@@ -29,7 +29,7 @@ import Control.Applicative
29--
29--
30-- >>> foo <- SomeWord <$> mkWord "foo"
30-- >>> foo <- SomeWord <$> mkWord "foo"
31-- >>> bar <- SomeWord <$> mkAcronym "bar"
31-- >>> bar <- SomeWord <$> mkAcronym "bar"
32−-- >>> bazz <- SomeWord <$> mkWord "bazz"
32+-- >>> bazz <- SomeWord <$> mkAcronym "bazz"
33-- >>> camelize [foo,bar,bazz]
33-- >>> camelize [foo,bar,bazz]
34-- "FoobarBazz"
34-- "FoobarBazz"
35camelize
35camelize
@@ -41,7 +41,7 @@ camelize = camelizeCustom True
41--
41--
42-- >>> foo <- SomeWord <$> mkWord "foo"
42-- >>> foo <- SomeWord <$> mkWord "foo"
43-- >>> bar <- SomeWord <$> mkAcronym "bar"
43-- >>> bar <- SomeWord <$> mkAcronym "bar"
44−-- >>> bazz <- SomeWord <$> mkWord "bazz"
44+-- >>> bazz <- SomeWord <$> mkAcronym "bazz"
45-- >>> camelizeCustom False [foo,bar,bazz]
45-- >>> camelizeCustom False [foo,bar,bazz]
46-- "foobarBazz"
46-- "foobarBazz"
47camelizeCustom
47camelizeCustom
Text/Inflections/Humanize.hs+3−24
@@ -13,8 +13,7 @@
13{-# LANGUAGE OverloadedStrings #-}
13{-# LANGUAGE OverloadedStrings #-}
14
14
15module Text.Inflections.Humanize
15module Text.Inflections.Humanize
16− ( humanize
16+ ( humanize )
17− , humanizeCustom )
18where
17where
19
18
20import Data.Text (Text)
19import Data.Text (Text)
@@ -37,27 +36,7 @@ import Control.Applicative
37humanize
36humanize
38 :: [SomeWord] -- ^ List of words, first of which will be capitalized
37 :: [SomeWord] -- ^ List of words, first of which will be capitalized
39 -> Text -- ^ The humanized output
38 -> Text -- ^ The humanized output
40−humanize = humanizeCustom True
39+humanize xs' =
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' =
61 case unSomeWord (T.replace "_" " ") <$> xs' of
40 case unSomeWord (T.replace "_" " ") <$> xs' of
62 [] -> ""
41 [] -> ""
63− (x:xs) -> T.unwords $ (if c then T.toTitle else T.toLower) x : (T.toLower <$> xs)
42+ (x:xs) -> T.unwords $ T.toTitle x : (T.toLower <$> xs)
Text/Inflections/Ordinal.hs+1
@@ -16,6 +16,7 @@ module Text.Inflections.Ordinal
16 , ordinal )
16 , ordinal )
17where
17where
18
18
19+import Data.Monoid ((<>))
19import Data.Text (Text)
20import Data.Text (Text)
20import qualified Data.Text as T
21import qualified Data.Text as T
21
22
Text/Inflections/Parse/CamelCase.hs+10−22
@@ -17,27 +17,17 @@ module Text.Inflections.Parse.CamelCase
17 ( parseCamelCase )
17 ( parseCamelCase )
18where
18where
19
19
20−# if MIN_VERSION_base(4,8,0)
21−import Control.Applicative (empty, many, (<|>))
20+import Control.Applicative
22−#else
23−import Control.Applicative (empty, many, (<|>), (<$>), (<*))
24−#endif
25import Data.Text (Text)
21import Data.Text (Text)
26−import Data.Void (Void)
27import Text.Inflections.Types
22import Text.Inflections.Types
28−import Text.Megaparsec (Parsec, ParseErrorBundle, choice, eof, parse)
23+import Text.Megaparsec
29−import Text.Megaparsec.Char
24+import Text.Megaparsec.Text
30import qualified Data.Text as T
25import qualified Data.Text as T
31
26
32#if MIN_VERSION_base(4,8,0)
27#if MIN_VERSION_base(4,8,0)
33import Prelude hiding (Word)
28import Prelude hiding (Word)
34−#else
35−import Data.Foldable
36−import Prelude hiding (elem)
37#endif
29#endif
38
30
39−type Parser = Parsec Void Text
40−
41-- | Parse a CamelCase string.
31-- | Parse a CamelCase string.
42--
32--
43-- >>> bar <- mkAcronym "bar"
33-- >>> bar <- mkAcronym "bar"
@@ -48,25 +38,23 @@ type Parser = Parsec Void Text
48-- 1:4:
38-- 1:4:
49-- unexpected '_'
39-- unexpected '_'
50-- expecting end of input, lowercase letter, or uppercase letter
40-- expecting end of input, lowercase letter, or uppercase letter
51−parseCamelCase :: (Foldable f, Functor f)
41+parseCamelCase
52− => f (Word 'Acronym) -- ^ Collection of acronyms
42+ :: [Word 'Acronym] -- ^ Collection of acronyms
53 -> Text -- ^ Input
43 -> Text -- ^ Input
54− -> Either (ParseErrorBundle Text Void) [SomeWord] -- ^ Result of parsing
44+ -> Either (ParseError Char Dec) [SomeWord] -- ^ Result of parsing
55parseCamelCase acronyms = parse (parser acronyms) ""
45parseCamelCase acronyms = parse (parser acronyms) ""
56
46
57−parser :: (Foldable f, Functor f)
47+parser
58− => f (Word 'Acronym) -- ^ Collection of acronyms
48+ :: [Word 'Acronym] -- ^ Collection of acronyms
59 -> Parser [SomeWord] -- ^ CamelCase parser
49 -> Parser [SomeWord] -- ^ CamelCase parser
60parser acronyms = many (a <|> n) <* eof
50parser acronyms = many (a <|> n) <* eof
61 where
51 where
62 n = SomeWord <$> word
52 n = SomeWord <$> word
63 a = SomeWord <$> acronym acronyms
53 a = SomeWord <$> acronym acronyms
64
54
65−acronym :: (Foldable f, Functor f)
66− => f (Word 'Acronym)
67− -> Parser (Word 'Acronym)
55+acronym :: [Word 'Acronym] -> Parser (Word 'Acronym)
68acronym acronyms = do
56acronym acronyms = do
69− x <- choice (string . unWord <$> acronyms)
57+ x <- T.pack <$> choice (string . T.unpack . unWord <$> acronyms)
70 case mkAcronym x of
58 case mkAcronym x of
71 Nothing -> empty -- cannot happen if the system is sound
59 Nothing -> empty -- cannot happen if the system is sound
72 Just acr -> return acr
60 Just acr -> return acr
Text/Inflections/Parse/SnakeCase.hs+27−28
@@ -16,27 +16,17 @@ module Text.Inflections.Parse.SnakeCase
16 ( parseSnakeCase )
16 ( parseSnakeCase )
17where
17where
18
18
19−# if MIN_VERSION_base(4,8,0)
20−import Control.Applicative (empty, some)
19+import Control.Applicative
21−#else
22−import Control.Applicative (empty, some, (<$>), (<*))
23−#endif
24import Data.Text (Text)
20import Data.Text (Text)
25−import Data.Void (Void)
26import Text.Inflections.Types
21import Text.Inflections.Types
27−import Text.Megaparsec (Parsec, ParseErrorBundle, eof, sepBy, parse)
22+import Text.Megaparsec
28−import Text.Megaparsec.Char
23+import Text.Megaparsec.Text
29import qualified Data.Text as T
24import qualified Data.Text as T
30
25
31#if MIN_VERSION_base(4,8,0)
26#if MIN_VERSION_base(4,8,0)
32import Prelude hiding (Word)
27import Prelude hiding (Word)
33−#else
34−import Data.Foldable
35−import Prelude hiding (elem)
36#endif
28#endif
37
29
38−type Parser = Parsec Void Text
39−
40-- | Parse a snake_case string.
30-- | Parse a snake_case string.
41--
31--
42-- >>> bar <- mkAcronym "bar"
32-- >>> bar <- mkAcronym "bar"
@@ -47,23 +37,32 @@ type Parser = Parsec Void Text
47-- 1:4:
37-- 1:4:
48-- unexpected 'B'
38-- unexpected 'B'
49-- expecting '_', end of input, or lowercase letter
39-- expecting '_', end of input, or lowercase letter
50−parseSnakeCase :: (Foldable f, Functor f)
40+parseSnakeCase
51− => f (Word 'Acronym) -- ^ Collection of acronyms
41+ :: [Word 'Acronym] -- ^ Collection of acronyms
52 -> Text -- ^ Input
42 -> Text -- ^ Input
53− -> Either (ParseErrorBundle Text Void) [SomeWord] -- ^ Result of parsing
43+ -> Either (ParseError Char Dec) [SomeWord] -- ^ Result of parsing
54parseSnakeCase acronyms = parse (parser acronyms) ""
44parseSnakeCase acronyms = parse (parser acronyms) ""
55
45
56−parser :: (Foldable f, Functor f)
46+parser
57− => f (Word 'Acronym)
47+ :: [Word 'Acronym]
58 -> Parser [SomeWord]
48 -> Parser [SomeWord]
59−parser acronyms = (pWord acronyms `sepBy` char '_') <* eof
49+parser acronyms = ((a <|> n) `sepBy` char '_') <* eof
50+ where
51+ n = SomeWord <$> word
52+ a = SomeWord <$> acronym acronyms
53+
54+acronym :: [Word 'Acronym] -> Parser (Word 'Acronym)
55+acronym acronyms = do
56+ x <- T.pack <$> choice (string . T.unpack . unWord <$> acronyms)
57+ case mkAcronym x of
58+ Nothing -> empty -- cannot happen if the system is sound
59+ Just acr -> return acr
60+{-# INLINE acronym #-}
60
61
61−pWord :: (Foldable f, Functor f)
62+word :: Parser (Word 'Normal)
62− => f (Word 'Acronym)
63+word = do
63− -> Parser SomeWord
64+ x <- T.pack <$> (some lowerChar <|> some digitChar)
64−pWord acronyms = do
65+ case mkWord x of
65− let acs = unWord <$> acronyms
66+ Nothing -> empty -- cannot happen if the system is sound
66− r <- T.pack <$> some alphaNumChar
67+ Just wrd -> return wrd
67− if r `elem` acs
68+{-# INLINE word #-}
68− then maybe empty (return . SomeWord) (mkAcronym r)
69− else maybe empty (return . SomeWord) (mkWord r)
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 'SomeWord' words in the input list.
20+-- | Capitalize all the 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, of which all 'SomeWord' words will be capitalized and all acronyms will be left as is
28+ :: [SomeWord] -- ^ List of words, first of which will be capitalized
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+13−19
@@ -31,9 +31,8 @@ module Text.Inflections.Types
31where
31where
32
32
33import Control.Monad.Catch
33import Control.Monad.Catch
34−import Data.Char (isAlphaNum)
34+import Data.Char (isSpace)
35import Data.Data (Data)
35import Data.Data (Data)
36−import Data.Void (Void)
37import Data.Text (Text)
36import Data.Text (Text)
38import Data.Typeable (Typeable)
37import Data.Typeable (Typeable)
39import GHC.Generics (Generic)
38import GHC.Generics (Generic)
@@ -44,30 +43,28 @@ import qualified Data.Text as T
44import Prelude hiding (Word)
43import Prelude hiding (Word)
45#endif
44#endif
46
45
47−-- | Create a word from given 'Text'. The input should consist of only
46+-- | Create a word from given 'Text'. The input should not contain spaces or
48−-- alpha-numeric characters (no white spaces or punctuation)
49-- 'InflectionInvalidWord' will be thrown.
47-- 'InflectionInvalidWord' will be thrown.
50--
48--
51-- /since 0.3.0.0/
49-- /since 0.3.0.0/
52
50
53mkWord :: MonadThrow m => Text -> m (Word 'Normal)
51mkWord :: MonadThrow m => Text -> m (Word 'Normal)
54mkWord txt =
52mkWord txt =
55− if T.all isAlphaNum txt
53+ if T.any isSpace txt
54+ then throwM (InflectionInvalidWord txt)
56− then return (Word txt)
55+ else return (Word txt)
57− else throwM (InflectionInvalidWord txt)
56+
58−
59−-- | Create an acronym from given 'Text'. The input should consist of only
57+-- | Create an acronym from given 'Text'. The input should not contain
60−-- alpha-numeric characters 'InflectionInvalidAcronym' will be thrown.
58+-- spaces or 'InflectionInvalidAcronym' will be thrown. Acronym is different
61−-- Acronym is different from normal word by that it may not be transformed
59+-- from normal word by that it may not be transformed by inflections.
62−-- by inflections (also see 'unSomeWord').
63--
60--
64-- /since 0.3.0.0/
61-- /since 0.3.0.0/
65
62
66mkAcronym :: MonadThrow m => Text -> m (Word 'Acronym)
63mkAcronym :: MonadThrow m => Text -> m (Word 'Acronym)
67mkAcronym txt =
64mkAcronym txt =
68− if T.all isAlphaNum txt
65+ if T.any isSpace txt
66+ then throwM (InflectionInvalidAcronym txt)
69− then return (Word txt)
67+ else return (Word txt)
70− else throwM (InflectionInvalidAcronym txt)
71
68
72-- | A 'Text' value that should be kept whole through applied inflections.
69-- | A 'Text' value that should be kept whole through applied inflections.
73
70
@@ -109,9 +106,6 @@ data SomeWord where
109instance Show SomeWord where
106instance Show SomeWord where
110 show (SomeWord w) = show w
107 show (SomeWord w) = show w
111
108
112−instance Eq SomeWord where
113− x == y = unSomeWord id x == unSomeWord id y
114−
115-- | Extract 'Text' from 'SomeWord' and apply given function only if the
109-- | Extract 'Text' from 'SomeWord' and apply given function only if the
116-- word inside wasn't an acronym.
110-- word inside wasn't an acronym.
117--
111--
@@ -136,7 +130,7 @@ instance Transformable (Word 'Acronym) where
136-- /since 0.3.0.0/
130-- /since 0.3.0.0/
137
131
138data InflectionException
132data InflectionException
139− = InflectionParsingFailed (ParseErrorBundle Text Void)
133+ = InflectionParsingFailed (ParseError Char Dec)
140 | InflectionInvalidWord Text
134 | InflectionInvalidWord Text
141 | InflectionInvalidAcronym Text
135 | InflectionInvalidAcronym Text
142 deriving (Eq, Show, Typeable, Data, Generic)
136 deriving (Eq, Show, Typeable, Data, Generic)
docs/CODE_OF_CONDUCT.mddeleted−35
@@ -1,35 +0,0 @@
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.mddeleted−13
@@ -1,13 +0,0 @@
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+9−23
@@ -1,5 +1,5 @@
1name: inflections
1name: inflections
2−version: 0.4.0.6
2+version: 0.2.0.1
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,13 +48,10 @@ 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
51+ , exceptions >= 0.6 && < 0.9
52− , megaparsec
52+ , megaparsec >= 5.0 && < 6.0
53− , text
53+ , text >= 0.2 && < 1.3
54− , unordered-containers
54+ , unordered-containers >= 0.2.7 && < 0.3
55− if !impl(ghc >= 7.10)
56− build-depends: void == 0.7.*
57−
58 default-language: Haskell2010
55 default-language: Haskell2010
59
56
60test-suite test
57test-suite test
@@ -62,19 +59,11 @@ test-suite test
62 hs-source-dirs: test
59 hs-source-dirs: test
63 main-is: Spec.hs
60 main-is: Spec.hs
64 build-depends: inflections
61 build-depends: inflections
65− , QuickCheck
62+ , QuickCheck >= 2.7.6 && < 3.0
66 , base >= 4.6 && < 5.0
63 , base >= 4.6 && < 5.0
67− , containers
64+ , hspec >= 2.0 && < 3.0
68− , hspec
69− , hspec-megaparsec
65+ , megaparsec >= 5.0 && < 6.0
70− , megaparsec
71− , text
66+ , text >= 0.2 && < 1.3
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−
78 if flag(dev)
67 if flag(dev)
79 ghc-options: -Wall -Werror
68 ghc-options: -Wall -Werror
80 else
69 else
@@ -84,10 +73,7 @@ test-suite test
84 , Text.Inflections.HumanizeSpec
73 , Text.Inflections.HumanizeSpec
85 , Text.Inflections.OrdinalSpec
74 , Text.Inflections.OrdinalSpec
86 , Text.Inflections.ParametrizeSpec
75 , Text.Inflections.ParametrizeSpec
87− , Text.Inflections.Parse.CamelCaseSpec
88− , Text.Inflections.Parse.SnakeCaseSpec
89 , Text.Inflections.TitleizeSpec
76 , Text.Inflections.TitleizeSpec
90 , Text.Inflections.TransliterateSpec
77 , Text.Inflections.TransliterateSpec
91− , Text.Inflections.TypesSpec
92 , Text.Inflections.UnderscoreSpec
78 , Text.Inflections.UnderscoreSpec
93 , Text.InflectionsSpec
79 , Text.InflectionsSpec
stack.yaml+3−1
@@ -1 +1,3 @@
1−resolver: lts-21.3
1+resolver: lts-7.13
2+packages:
3+- '.'
stack.yaml.lockdeleted−12
@@ -1,12 +0,0 @@
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+12−40
@@ -12,43 +12,15 @@ import Control.Applicative
12#endif
12#endif
13
13
14spec :: Spec
14spec :: Spec
15−spec = do
16− describe "humanize" $ do
15+spec = describe "humazine" $ do
17− it "converts snake case to a human-readable string" $ do
16+ it "converts snake case to a human-readable string" $ do
18− employee <- SomeWord <$> mkWord "employee"
17+ employee <- SomeWord <$> mkWord "employee"
19− salary <- SomeWord <$> mkWord "salary"
18+ salary <- SomeWord <$> mkWord "salary"
20− humanize [employee,salary] `shouldBe` "Employee salary"
19+ humanize [employee,salary] `shouldBe` "Employee salary"
21− it "turns underscores into spaces" $ do
20+ it "turns underscores into spaces" $ do
22− employee <- SomeWord <$> mkWord "employee"
21+ employee <- SomeWord <$> mkWord "employee"
23− has <- SomeWord <$> mkWord "has"
22+ hasSalary <- SomeWord <$> mkWord "has_salary"
24− salary <- SomeWord <$> mkWord "salary"
25− humanize [employee, has, salary] `shouldBe` "Employee has salary"
23+ humanize [employee, hasSalary] `shouldBe` "Employee has salary"
26− it "capitalizes the first word of a sentence" $ do
24+ it "capitalizes the first word of a sentence" $ do
27− underground <- SomeWord <$> mkWord "underground"
25+ 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"
54− humanizeCustom True [underground] `shouldBe` "Underground"
26+ humanize [underground] `shouldBe` "Underground"
test/Text/Inflections/Parse/CamelCaseSpec.hsdeleted−47
@@ -1,47 +0,0 @@
1−{-# LANGUAGE OverloadedStrings #-}
2−
3−module Text.Inflections.Parse.CamelCaseSpec
4− ( spec )
5−where
6−
7−import Test.Hspec
8−import Test.Hspec.Megaparsec
9−import Text.Inflections
10−
11−spec :: Spec
12−spec =
13− describe "parseCamelCase" $ do
14− context "when given no acronyms" $ do
15− context "when first word is capitalized" $
16− it "parses CamelCase correctly" $ do
17− r <- mapM (fmap SomeWord . mkWord) ["One","Two","Three"]
18− parseCamelCase [] "OneTwoThree" `shouldParse` r
19− context "when first word is not capitalized" $
20− it "parses camelCase correctly" $ do
21− r <- mapM (fmap SomeWord . mkWord) ["one","Two","Three"]
22− parseCamelCase [] "oneTwoThree" `shouldParse` r
23− context "when there are digits in the words" $
24− it "parses CamelCase correctly" $ do
25− r <- mapM (fmap SomeWord . mkWord) ["one1two","Three3"]
26− parseCamelCase [] "one1twoThree3" `shouldParse` r
27− context "when given some acronyms" $ do
28− context "when first word is capitalized" $
29− it "parses CamelCase correctly" $ do
30− a <- mkAcronym "BOO"
31− r <- mapM (fmap SomeWord . mkWord) ["One","BOO","One"]
32− parseCamelCase [a] "OneBOOOne" `shouldParse` r
33− context "when first word is not capitalized" $
34− it "parses camelCase correctly" $ do
35− a <- mkAcronym "BOO"
36− r <- mapM (fmap SomeWord . mkWord) ["one","Two","BOO"]
37− parseCamelCase [a] "oneTwoBOO" `shouldParse` r
38− context "when there are digits in the words" $
39− it "parses CamelCase correctly" $ do
40− a <- mkAcronym "BOO"
41− r <- mapM (fmap SomeWord . mkWord) ["one1two","Three3"]
42− parseCamelCase [a] "one1twoThree3" `shouldParse` r
43− context "when a word has a suffix coinciding with a acronym" $
44− it "is still parsed correctly as a normala word" $ do
45− a <- mkAcronym "boo"
46− r <- mapM (fmap SomeWord . mkWord) ["fooboo","Bar"]
47− parseCamelCase [a] "foobooBar" `shouldParse` r
test/Text/Inflections/Parse/SnakeCaseSpec.hsdeleted−35
@@ -1,35 +0,0 @@
1−{-# LANGUAGE OverloadedStrings #-}
2−
3−module Text.Inflections.Parse.SnakeCaseSpec
4− ( spec )
5−where
6−
7−import Test.Hspec
8−import Test.Hspec.Megaparsec
9−import Text.Inflections
10−
11−spec :: Spec
12−spec =
13− describe "parseSnakeCase" $ do
14− context "when given no acronyms" $ do
15− it "parses snake_case correctly" $ do
16− r <- mapM (fmap SomeWord . mkWord) ["OneTwo","Three"]
17− parseSnakeCase [] "OneTwo_Three" `shouldParse` r
18− it "handles digits in the words correctly" $ do
19− r <- mapM (fmap SomeWord . mkWord) ["one4a","two00"]
20− parseSnakeCase [] "one4a_two00" `shouldParse` r
21− context "when given some acronyms" $ do
22− it "parses snake_case correctly" $ do
23− a <- mkAcronym "BOO"
24− r <- mapM (fmap SomeWord . mkWord) ["BOO","one","one"]
25− parseSnakeCase [a] "BOO_one_one" `shouldParse` r
26− context "when acronym happens to be a prefix of other word" $
27− it "parses the word correctly" $ do
28− a <- mkAcronym "BOO"
29− r <- mapM (fmap SomeWord . mkWord) ["one","BOOtwo"]
30− parseSnakeCase [a] "one_BOOtwo" `shouldParse` r
31− context "when acronym happens to be a suffix of other word" $
32− it "parses the word correctly" $ do
33− a <- mkAcronym "BOO"
34− r <- mapM (fmap SomeWord . mkWord) ["oneBOO","two"]
35− parseSnakeCase [a] "oneBOO_two" `shouldParse` r
test/Text/Inflections/TypesSpec.hsdeleted−51
@@ -1,51 +0,0 @@
1−{-# LANGUAGE CPP #-}
2−{-# LANGUAGE OverloadedStrings #-}
3−
4−module Text.Inflections.TypesSpec
5− ( spec )
6−where
7−
8−import Test.Hspec
9−import Text.Inflections
10−import qualified Data.Text as T
11−
12−#if !MIN_VERSION_base(4,8,0)
13−import Control.Applicative
14−#endif
15−
16−spec :: Spec
17−spec = do
18− describe "mkWord" $ do
19− context "when provided a correct Text value" $
20− it "creates a normal word" $ do
21− x <- mkWord "foo"
22− unWord x `shouldBe` "foo"
23− context "when provided an incorrect Text value" $
24− it "throws the correct exception" $ do
25− mkWord "foo bar" `shouldThrow` (== InflectionInvalidWord "foo bar")
26− mkWord "foo_" `shouldThrow` (== InflectionInvalidWord "foo_")
27− mkWord "&$?%" `shouldThrow` (== InflectionInvalidWord "&$?%")
28− describe "mkAcronym" $ do
29− context "when provided a correct Text value" $
30− it "creates an acronym" $ do
31− x <- mkAcronym "foo"
32− unWord x `shouldBe` "foo"
33− context "when providde an incorrect Text value" $
34− it "throws the correct exception" $ do
35− mkAcronym "foo bar" `shouldThrow` (== InflectionInvalidAcronym "foo bar")
36− mkAcronym "foo_" `shouldThrow` (== InflectionInvalidAcronym "foo_")
37− mkAcronym "&$?%" `shouldThrow` (== InflectionInvalidAcronym "&$?%")
38− describe "unWord" $
39− it "extracts the inner Text value" $ do
40− (unWord <$> mkWord "foo") `shouldReturn` "foo"
41− (unWord <$> mkWord "bar") `shouldReturn` "bar"
42− (unWord <$> mkAcronym "baz") `shouldReturn` "baz"
43− describe "unSomeWord" $ do
44− context "when inner value is a normal word" $
45− it "Text is extracted and the given function applied" $ do
46− x <- SomeWord <$> mkWord "word"
47− unSomeWord T.toUpper x `shouldBe` "WORD"
48− context "when inner value is an acronym" $
49− it "Text is extracted, but the function is not applied" $ do
50− x <- SomeWord <$> mkAcronym "acronym"
51− unSomeWord T.toUpper x `shouldBe` "acronym"
test/Text/InflectionsSpec.hs+2−76
@@ -1,98 +1,24 @@
1−{-# OPTIONS_GHC -fno-warn-orphans #-}
2−{-# LANGUAGE CPP #-}
3{-# LANGUAGE OverloadedStrings #-}
1{-# LANGUAGE OverloadedStrings #-}
4
2
5module Text.InflectionsSpec (spec) where
3module Text.InflectionsSpec (spec) where
6
4
7−import Data.Void
8import Test.Hspec
5import Test.Hspec
9−import Test.QuickCheck
10−import Text.Inflections
6+import Text.Inflections (toUnderscore, toDashed, toCamelCased)
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)
58
7
59spec :: Spec
8spec :: Spec
60spec = do
9spec = do
61−
62 describe "toUnderscore" $ do
10 describe "toUnderscore" $ do
63 it "converts camel case to snake case" $
11 it "converts camel case to snake case" $
64 toUnderscore "camelCasedText" `shouldBe` Right "camel_cased_text"
12 toUnderscore "camelCasedText" `shouldBe` Right "camel_cased_text"
65 it "converts camel case to snake case with numbers" $
13 it "converts camel case to snake case with numbers" $
66 toUnderscore "ipv4Address" `shouldBe` Right "ipv4_address"
14 toUnderscore "ipv4Address" `shouldBe` Right "ipv4_address"
67−
68 describe "toDashed" $
15 describe "toDashed" $
69 it "converts camel case to dashed" $
16 it "converts camel case to dashed" $
70 toDashed "camelCasedText" `shouldBe` Right "camel-cased-text"
17 toDashed "camelCasedText" `shouldBe` Right "camel-cased-text"
71−
72 describe "toCamelCased" $ do
18 describe "toCamelCased" $ do
73 context "when the first argument is False" $
19 context "when the first argument is False" $
74 it "converts snake case to camel case" $
20 it "converts snake case to camel case" $
75 toCamelCased False "underscored_text" `shouldBe` Right "underscoredText"
21 toCamelCased False "underscored_text" `shouldBe` Right "underscoredText"
76 context "when the first argument is True" $
22 context "when the first argument is True" $
77− it "converts snake case to camel case with the first word capitalized" $
23+ it "converts snake case to camel case" $
78 toCamelCased True "underscored_text" `shouldBe` Right "UnderscoredText"
24 toCamelCased True "underscored_text" `shouldBe` Right "UnderscoredText"
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−
88− describe "betterThrow" $ do
89− context "when given a parse error" $
90− it "throws the correct exception" $
91− property $ forAll arbitraryParseErrorBundle $ \err ->
92− betterThrow (Left err) `shouldThrow`
93− (== InflectionParsingFailed err)
94−
95− context "when given a value in Right" $
96− it "returns the value" $
97− property $ \x ->
98− betterThrow (Right x) `shouldReturn` (x :: Int)