Commits

Gabe McArthur  committed c4e92aa

Adding functions to TH to make CamelCase to under_score easier.

I have noticed in multiple places around hackage that people add
these kinds of functions to get their JSON formatted keys (often
underscore_formatted_strings) out of Haskell's CamelCase data
properties (often withLongStringsOfCAPITALS).

I have also added to the .gitignore those files generated by the new
cabal 1.18 'sandbox' feature.

I have a few outstanding questions:

1) Should these functions live in another module with an isomorphic
inverse function, like `underscoreToCamel`? Like Aeson.Properties?
2) I am at something of a loss as to how to integrate testing into the
suite. I've tested the out at the REPL, but what steps should be
taken to integrate these into QuickCheck?

  • Participants
  • Parent commits 7a2d130

Comments (0)

Files changed (2)

 dist
+.cabal-sandbox/
+cabal.sandbox.config
 
 *.o
 *.hi

File Data/Aeson/TH.hs

 
     , mkToJSON
     , mkParseJSON
+
+    -- * For the oft-conversion of CamelCase to underscore_case in JSON
+    , camelToUnderscore
+    , camelToUnderscoreDrop
+    , camelToUnderscoreDropChecked
     ) where
 
 --------------------------------------------------------------------------------
 -- from base:
 import Control.Applicative ( pure, (<$>), (<*>) )
 import Control.Monad       ( return, mapM, liftM2, fail )
+import Data.Char           ( isUpper, toLower)
 import Data.Bool           ( Bool(False, True), otherwise, (&&) )
 import Data.Eq             ( (==) )
 import Data.Function       ( ($), (.) )
 import Data.Int            ( Int )
 import Data.Either         ( Either(Left, Right) )
 import Data.List           ( (++), foldl, foldl', intercalate
-                           , length, map, zip, genericLength, all, partition
+                           , length, map, zip, genericLength, all, partition, drop
                            )
 import Data.Maybe          ( Maybe(Nothing, Just), catMaybes )
-import Prelude             ( String, (-), Integer, fromIntegral, error )
+import Prelude             ( String, (-), Integer, fromIntegral, error, const )
 import Text.Printf         ( printf )
 import Text.Show           ( show )
 #if __GLASGOW_HASKELL__ < 700
 valueConName (Number _) = "Number"
 valueConName (Bool   _) = "Boolean"
 valueConName Null       = "Null"
+
+-- | Removes the first Int characters of string, then converts from
+--   CamelCase to underscore_case.
+--
+--   For use by Aeson template haskell calls.
+camelToUnderscore :: String -> String
+camelToUnderscore = camelToUnderscoreDrop 0
+
+-- | Removes the first Int characters of string, then converts from
+--   CamelCase to underscore_case. Drops the first few characters from
+--   the input.
+--
+--   For use by Aeson template haskell calls.
+camelToUnderscoreDrop :: Int -> String -> String
+camelToUnderscoreDrop i = camelToUnderscoreDropChecked (const i)
+
+-- | Removes the first Int characters of string, then converts from
+--   CamelCase to underscore_case. Investigates the input string to
+--   see how many characters to drop.
+--
+--   For use by Aeson template haskell calls.
+camelToUnderscoreDropChecked :: (String -> Int) -> String -> String
+camelToUnderscoreDropChecked chk property =
+    underscore True input
+  where
+    input :: String
+    input = drop (chk property) property
+
+    underscore :: Bool    -- ^ Previous was a capital letter
+               -> String  -- ^ The remaining string
+               -> String
+    underscore _    []           = []
+    underscore prev (x : xs)     = if isUpper x
+                                      then if prev
+                                             then toLower x : underscore True xs
+                                             else '_' : toLower x : underscore True xs
+                                      else x : underscore False xs
+