safe-json 1.0.0 → 1.1.0
raw patch · 9 files changed
+65/−56 lines, 9 filesdep ~aesondep ~dlistdep ~safe-jsonPVP ok
version bump matches the API change (PVP)
Dependency ranges changed: aeson, dlist, safe-json, tasty, time
API changes (from Hackage documentation)
- Data.SafeJSON.Test: type TestMigrate a b = (Eq a, Show (MigrateFrom a), Arbitrary (MigrateFrom a), SafeJSON a, SafeJSON (MigrateFrom a), Migrate a, MigrateFrom a ~ b)
+ Data.SafeJSON.Test: type TestMigrate a b = (Eq a, Show b, Arbitrary b, SafeJSON a, Migrate a, MigrateFrom a ~ b)
- Data.SafeJSON.Test: type TestReverseMigrate a b = (Eq a, Show (MigrateFrom (Reverse a)), Arbitrary (MigrateFrom (Reverse a)), SafeJSON a, Migrate (Reverse a), MigrateFrom (Reverse a) ~ b)
+ Data.SafeJSON.Test: type TestReverseMigrate a b = (Eq a, Show b, Arbitrary b, SafeJSON a, Migrate (Reverse a), MigrateFrom (Reverse a) ~ b)
Files
- ChangeLog.md +10/−0
- safe-json.cabal +5/−5
- src/Data/SafeJSON.hs +8/−9
- src/Data/SafeJSON/Internal.hs +28/−30
- src/Data/SafeJSON/Test.hs +8/−9
- test/Consistency/Primitives.hs +0/−1
- test/MigrationTests.hs +5/−0
- test/PrimitiveTests.hs +0/−1
- test/json/primitives.json +1/−1
ChangeLog.md view
@@ -1,5 +1,15 @@ # Changelog for safe-json +## 1.1.0++* update for GHC 8.8.1 [#15]+ * loosened dependency restriction on `time`+ * fixed instance for IntMap+* DRY-er `TestMigrate` and `TestReverseMigrate` type synonyms [#17] Thanks to @blinkytoy+* fixed documentation [#17] Thanks to @blinkytoy+ * broken links to modules+ * `setVersion`'s documentation only showing half+ ## 1.0.0 * Removed `FromJSON`/`ToJSON` dependecy on `SafeJSON`
safe-json.cabal view
@@ -4,10 +4,10 @@ -- -- see: https://github.com/sol/hpack ----- hash: 57d54c49f4f8ea15d008d2d770db3fee6e057d771456bdc6309ffcb8cecd5637+-- hash: 5d912c70ee9329eebd7665ea92fcacb1ae56b1d590ddecf5abeb7d800e270bf3 name: safe-json-version: 1.0.0+version: 1.1.0 synopsis: Automatic JSON format versioning description: This library aims to make the updating of JSON formats or contents, while keeping backward compatibility, as painless as possible. The way this is achieved is through versioning and defined migration functions to migrate older (or newer) versions to the one used. .@@ -72,7 +72,7 @@ , tasty-hunit >=0.9.2 && <0.11 , tasty-quickcheck >=0.8.4 && <0.11 , text >=1.2.3 && <1.3- , time >=1.6.0.1 && <1.9+ , time >=1.6.0.1 && <1.10 , unordered-containers >=0.2.9 && <0.3 , uuid-types >=1.0.3 && <1.1 , vector >=0.12.0.1 && <0.13@@ -106,14 +106,14 @@ , generic-arbitrary >=0.1.0 && <0.2 , hashable >=1.2.6.1 && <1.4 , quickcheck-instances >=0.3.16 && <0.4- , safe-json >=1.0 && <1.1+ , safe-json >=1.0 && <1.2 , scientific >=0.3.5.2 && <0.4 , tasty , tasty-hunit , tasty-quickcheck , temporary >=1.2.1.1 && <1.4 , text >=1.2.3 && <1.3- , time >=1.6.0.1 && <1.9+ , time >=1.6.0.1 && <1.10 , unordered-containers >=0.2.9 && <0.3 , uuid >=1.3.13 && <1.4 , uuid-types >=1.0.3 && <1.1
src/Data/SafeJSON.hs view
@@ -1,5 +1,5 @@ {-|-Module : Data.SafeJSON.Instances+Module : Data.SafeJSON Copyright : (c) 2019 Felix Paulusma License : MIT Maintainer : felix.paulusma@gmail.com@@ -7,7 +7,7 @@ Please read the -__[README on GitHub](https://github.com/Vlix/safe-json/blob/v1.0.0/README.md)__+__[README on GitHub](https://github.com/Vlix/safe-json/blob/v1.1.0/README.md)__ for an extensive explanation of this library, why and how to use it, and examples.@@ -58,8 +58,8 @@ -- -- | The following functions are helpful when defining 'safeFrom'. -- They are basically 'contain' composed with the corresponding- -- 'Data.Aeson' function, so they can be used in the same fashion- -- as said 'Data.Aeson' function.+ -- "Data.Aeson" function, so they can be used in the same fashion+ -- as said "Data.Aeson" function. , containWithObject , containWithArray , containWithText@@ -67,7 +67,7 @@ , containWithBool -- *** Accessors --- -- | These accessors can be used like their 'Data.Aeson' counterparts.+ -- | These accessors can be used like their "Data.Aeson" counterparts. -- The only difference is that the expected value is parsed using -- 'safeFromJSON' instead of 'Data.Aeson.parseJSON'. , (.:$)@@ -76,7 +76,7 @@ -- *** Constructor for 'safeTo' -- -- | This constructor of key-value pairs can be used exactly like- -- its 'Data.Aeson' counterpart ('Data.Aeson..='), but converts the+ -- its "Data.Aeson" counterpart ('Data.Aeson..='), but converts the -- given value with 'safeToJSON' instead of 'Data.Aeson.toJSON' , (.=$) -- ** Version@@ -104,12 +104,12 @@ -- * Extensions (see 'extension' and 'extended_extension') tell the -- system that there exists at least one previous version of the data -- type which should be migrated from if needed.- -- (This requires the data type to also have a @Migrate a@ instance)+ -- (This requires the data type to also have a @'Migrate' a@ instance) -- -- * Forward extensions (see 'extended_base' and 'extended_extension') -- tell the system there exists at least one next version from which -- the data type can be reverse-migrated.- -- (This requires the data type to also have a @Migrate (Reverse a)@+ -- (This requires the data type to also have a @'Migrate' ('Reverse' a)@ -- instance) , Kind , base@@ -135,4 +135,3 @@ ) where import Data.SafeJSON.Internal-import Data.Aeson
src/Data/SafeJSON/Internal.hs view
@@ -21,7 +21,6 @@ {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TypeSynonymInstances #-} {-# LANGUAGE UndecidableInstances #-}-{-# OPTIONS_GHC -Wno-redundant-constraints #-} {-| Module : Data.SafeJSON.Internal Copyright : (c) 2019 Felix Paulusma@@ -41,7 +40,6 @@ import Control.Monad.Fail (MonadFail) import Data.Aeson import Data.Aeson.Types (Parser, explicitParseField, explicitParseFieldMaybe, explicitParseFieldMaybe')-import Data.Char (Char) import Data.DList as DList (DList, fromList) import Data.Fixed (Fixed, HasResolution) import Data.Functor.Identity (Identity(..))@@ -53,7 +51,7 @@ import qualified Data.HashMap.Strict as HM (HashMap, delete, fromList, lookup, toList) import qualified Data.HashSet as HS (HashSet, fromList, toList) import Data.Int-import Data.IntMap (IntMap)+import Data.IntMap as IM (IntMap, fromList) import Data.IntSet (IntSet) import qualified Data.List as List (intercalate, lookup) import Data.List.NonEmpty (NonEmpty(..))@@ -162,8 +160,8 @@ -- | This instance is needed to handle the migration between -- older and newer versions. ----- Note that, where @(Migrate a)@ migrates from the previous--- version to the type @a@, @(Migrate (Reverse a))@ migrates+-- Note that, where @('Migrate' a)@ migrates from the previous+-- version to the type @a@, @('Migrate' ('Reverse' a))@ migrates -- from the future version to the type @a@. -- -- === __Example__@@ -171,22 +169,22 @@ -- __Two types that can migrate to each other.__ -- -- (Don't forget to give @OldType@ one of the @extended@ 'kind's,--- and @NewType@ one of the @extension@ kinds.)+-- and @NewType@ one of the @extension@ 'kind's.) -- -- @ -- instance 'Migrate' NewType where -- type 'MigrateFrom' NewType = OldType -- 'migrate' OldType = NewType ----- instance 'Migrate' (Reverse OldType) where--- type 'MigrateFrom' (Reverse OldType) = NewType--- 'migrate' NewType = Reverse OldType+-- instance 'Migrate' ('Reverse' OldType) where+-- type 'MigrateFrom' ('Reverse' OldType) = NewType+-- 'migrate' NewType = 'Reverse' OldType -- @ class SafeJSON (MigrateFrom a) => Migrate a where -- | The type from which will be migrated to type @a@ type MigrateFrom a -- | The migration from the previous version to the- -- current type @a@. OR, in case of a @(Reverse a)@,+ -- current type @a@. OR, in case of a @('Reverse' a)@, -- the migration from the future version back to -- the current type @a@ migrate :: MigrateFrom a -> a@@ -204,17 +202,10 @@ contain :: a -> Contained a contain = Contained -{---- Opens up mis-use of 'safeFrom' / 'safeTo', better to not-instance Applicative Contained where- pure = contain- Contained f <*> Contained a = Contained $ f a--}- -- | A simple numeric version id. -- -- 'Version' has a 'Num' instance and should be--- declared using integer literals: @version = 2@+-- declared using integer literals: @'version' = 2@ newtype Version a = Version {unVersion :: Maybe Int32} deriving (Eq) @@ -228,16 +219,16 @@ -- when a format is already in use, but you still want to -- be able to 'migrate' from it to a newer type or format. ----- /N.B./ @version = noVersion@ /is distinctively different/--- /from/ @version = 0@/, which will add a version tag with/+-- /N.B./ @'version' = 'noVersion'@ /is distinctively different/+-- /from/ @'version' = 0@/, which will add a version tag with/ -- /the number 0 (zero), whereas/ 'noVersion' /will not add a/--- /version tag./+-- /'version' tag./ noVersion :: Version a noVersion = Version Nothing -- | Same as 'setVersion', but requires a 'Version' parameter. ----- >>> 'encode' $ 'setVersion'' (version :: 'Version' Test) val+-- >>> 'encode' $ 'setVersion'' ('version' :: 'Version' Test) val -- "{\"~v\":0,\"~d\":\"test\"}" -- -- @since 1.0.0@@ -262,7 +253,7 @@ -- (cf. 'removeVersion') In some rare cases, you might want to interpret -- a versionless 'Value' as a certain type/version. 'setVersion' allows -- you to (unsafely) insert a version field.-+-- -- __If possible, it is advised to use a 'FromJSON' instance instead.__ -- (One that doesn't also use `safeFromJSON` in its methods!) --@@ -273,7 +264,7 @@ -- @ -- USAGE: ----- {-# LANGUAGE TypeApplications#-}+-- {-\# LANGUAGE TypeApplications \#-} -- data Test = Test String -- instance 'SafeJSON' Test where ... --@@ -281,9 +272,9 @@ -- String "test" -- >>> 'encode' val -- "\"test\""--- >>> 'encode' $ 'setVersion' @Test val+-- >>> 'encode' $ 'setVersion' \@Test val -- "{\"~v\":0,\"~d\":\"test\"}"--- >>> parseMaybe 'safeFromJSON' $ 'setVersion' @Test val+-- >>> parseMaybe 'safeFromJSON' $ 'setVersion' \@Test val -- Just (Test "test") -- @ --@@ -399,7 +390,7 @@ -- -- 'safeToJSON' will add a version tag to the 'Data.Aeson.Value' created. -- If the 'Data.Aeson.Value' resulting from 'safeTo' (by default the same as 'toJSON')--- is an @Object@, an extra field with the version number will be added.+-- is an @'Object'@, an extra field with the version number will be added. -- -- > Example value: -- > {"type":"test", "data":true}@@ -407,7 +398,7 @@ -- > Resulting object: -- > {"!v": 1, "type":"test", "data":true} ----- If the resulting 'Value' is not an @Object@, it will be wrapped+-- If the resulting 'Value' is not an @'Object'@, it will be wrapped -- in one, with a version field: -- -- > Example value:@@ -610,7 +601,7 @@ showVs = List.intercalate ", " . fmap go where go (mi, s) = mconcat ["(", showV mi, ", ", s, ")"] --- | @Version Nothing@ shows as @null@+-- | @'Version' Nothing@ shows as @null@ instance Show ProfileVersions where show (ProfileVersions cur sup) = mconcat [ "version ", showV cur, ": ["@@ -970,10 +961,17 @@ typeName = typeName1; \ version = noVersion } -BASIC_UNARY_FUNCTOR(IntMap) BASIC_UNARY_FUNCTOR(NonEmpty) BASIC_UNARY_FUNCTOR(Seq) BASIC_UNARY_FUNCTOR(Tree)++instance (SafeJSON a) => SafeJSON (IntMap a) where+ safeFrom val = contain $ do+ vs <- parseJSON val+ IM.fromList <$> mapM safeFromJSON vs+ safeTo as = contain . toJSON $ safeToJSON <$> as+ typeName = typeName1+ version = noVersion instance (SafeJSON a) => SafeJSON (DList a) where safeFrom val = contain $ do
src/Data/SafeJSON/Test.hs view
@@ -154,7 +154,7 @@ parseEither (safeFromJSON . safeToJSON) oldType -- | Similar to 'migrateRoundTrip', but tests the migration from a newer type--- to the older type, in case of a @Migrate (Reverse a)@ instance+-- to the older type, in case of a @'Migrate' ('Reverse' a)@ instance migrateReverseRoundTrip :: forall a. (Eq a, Show a, SafeJSON a, Migrate (Reverse a)) => MigrateFrom (Reverse a) -> Assertion migrateReverseRoundTrip newType = "Unexpected result of decoding encoded newer type" `assertEqual` Right (unReverse $ migrate newType :: a) $ parseEither (safeFromJSON . safeToJSON) newType@@ -162,10 +162,9 @@ -- | Constraints for migrating from a previous version type TestMigrate a b = ( Eq a- , Show (MigrateFrom a)- , Arbitrary (MigrateFrom a)+ , Show b+ , Arbitrary b , SafeJSON a- , SafeJSON (MigrateFrom a) , Migrate a , MigrateFrom a ~ b )@@ -195,15 +194,15 @@ -- | Constraints for migrating from a future version type TestReverseMigrate a b = ( Eq a- , Show (MigrateFrom (Reverse a))- , Arbitrary (MigrateFrom (Reverse a))+ , Show b+ , Arbitrary b , SafeJSON a , Migrate (Reverse a) , MigrateFrom (Reverse a) ~ b ) --- | Similar to 'migrateRoundTripProp, but tests the migration from a newer type--- to the older type, in case of a @Migrate (Reverse a)@ instance.+-- | Similar to 'migrateRoundTripProp', but tests the migration from a newer type+-- to the older type, in case of a @'Migrate' ('Reverse' a)@ instance. -- -- prop> Just (unReverse $ migrate a) == parseMaybe safeFromJSON (safeToJSON a) migrateReverseRoundTripProp' :: forall a b. TestReverseMigrate a b => Proxy (a,b) -> String -> TestTree@@ -211,7 +210,7 @@ Right (unReverse $ migrate a :: a) == parseEither (safeFromJSON . safeToJSON) a -- | Similar to 'migrateRoundTripProp', but tests the migration from a newer type--- to the older type, in case of a @Migrate (Reverse a)@ instance.+-- to the older type, in case of a @'Migrate' ('Reverse' a)@ instance. -- -- prop> Just (unReverse $ migrate a) == parseMaybe safeFromJSON (safeToJSON a) --
test/Consistency/Primitives.hs view
@@ -4,7 +4,6 @@ import Control.Applicative (Const) import Data.Aeson (DotNetTime, Value)-import Data.Char (Char) import Data.DList (DList) import Data.Fixed (E12, Fixed) import Data.Functor.Identity (Identity)
test/MigrationTests.hs view
@@ -1,4 +1,5 @@ {-# LANGUAGE AllowAmbiguousTypes #-}+{-# LANGUAGE CPP #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE ScopedTypeVariables #-}@@ -159,7 +160,11 @@ newtype ExpectedError = EE String deriving (Eq, Show) vNilError :: ExpectedError+#if MIN_VERSION_aeson(1,4,6)+vNilError = EE "Error in $: key \"int\" not found"+#else vNilError = EE "Error in $: key \"int\" not present"+#endif noParserError :: String -> ExpectedError noParserError = EE . mappend "Error in $: Cannot find parser associated with: "
test/PrimitiveTests.hs view
@@ -8,7 +8,6 @@ import Data.Aeson (DotNetTime, Value, (.:)) import qualified Data.Aeson as A import Data.Aeson.Types (parseEither)-import Data.Char (Char) import Data.DList (DList) import Data.Fixed (E12, Fixed) import Data.Functor.Identity (Identity)
test/json/primitives.json view
@@ -56,7 +56,7 @@ "Dual":"test", "[]":[1,2,3,4,5],- "IntMap":[[1,true],[9486,false]],+ "IntMap":[[1,true],[9486,false],[-1,false]], "NonEmpty":[12,3,5,76], "Seq":["one","two","three"], "Tree":["one",[["two",[]],["three",[]]]],