greskell 2.0.3.2 → 2.0.3.3
raw patch · 7 files changed
+87/−50 lines, 7 filesdep ~basePVP ok
version bump matches the API change (PVP)
Dependency ranges changed: base
API changes (from Hackage documentation)
Files
- ChangeLog.md +8/−0
- greskell.cabal +6/−6
- src/Data/Greskell/Binder.hs +5/−1
- src/Data/Greskell/Extra.hs +3/−3
- test/Data/Greskell/BinderSpec.hs +33/−24
- test/Data/Greskell/ExtraSpec.hs +3/−3
- test/ServerTest.hs +29/−13
ChangeLog.md view
@@ -1,5 +1,13 @@ # Revision history for greskell +## 2.0.3.3 -- 2025-01-30++* Binder: now `newBind` produces an expression like `((__v0))`, that is, a variable name wrapped with double parens.+ (see https://github.com/debug-ito/greskell/issues/18 )+ While this is basically backward-compatible, it can break a user code if it depends on the internal of the `Greskell` returned by `newBind`.+* Support ghc-9.12 (base-4.21).+ + ## 2.0.3.2 -- 2024-11-05 * Bump dependency version bounds.
greskell.cabal view
@@ -1,5 +1,5 @@ name: greskell-version: 2.0.3.2+version: 2.0.3.3 author: Toshio Ito <debug.ito@gmail.com> maintainer: Toshio Ito <debug.ito@gmail.com> license: BSD3@@ -40,7 +40,7 @@ Data.Greskell.NonEmptyLike, Data.Greskell.Logic -- other-modules: - build-depends: base ^>=4.13.0 || ^>=4.14.0 || ^>=4.15.0 || ^>=4.16.0 || ^>=4.17.0 || ^>=4.18.0 || ^>=4.19.0 || ^>=4.20.0,+ build-depends: base ^>=4.13.0 || ^>=4.14.0 || ^>=4.15.0 || ^>=4.16.0 || ^>=4.17.0 || ^>=4.18.0 || ^>=4.19.0 || ^>=4.20.0 || ^>=4.21.0, greskell-core ^>=1.0.0, text ^>=1.2.3 || ^>=2.0.2 || ^>=2.1, transformers ^>=0.5.6 || ^>=0.6.1,@@ -69,7 +69,7 @@ Data.Greskell.LogicSpec, ExamplesSpec build-tool-depends: hspec-discover:hspec-discover- build-depends: base ^>=4.13.0 || ^>=4.14.0 || ^>=4.15.0 || ^>=4.16.0 || ^>=4.17.0 || ^>=4.18.0 || ^>=4.19.0 || ^>=4.20.0,+ build-depends: base ^>=4.13.0 || ^>=4.14.0 || ^>=4.15.0 || ^>=4.16.0 || ^>=4.17.0 || ^>=4.18.0 || ^>=4.19.0 || ^>=4.20.0 || ^>=4.21.0, text ^>=1.2.3 || ^>=2.0.2 || ^>=2.1, aeson ^>=2.0.2 || ^>=2.1.0 || ^>=2.2.3, unordered-containers ^>=0.2.15,@@ -84,7 +84,7 @@ hs-source-dirs: test ghc-options: -Wall -fno-warn-unused-imports "-with-rtsopts=-M512m" main-is: Typecheck.hs- build-depends: base ^>=4.13.0 || ^>=4.14.0 || ^>=4.15.0 || ^>=4.16.0 || ^>=4.17.0 || ^>=4.18.0 || ^>=4.19.0 || ^>=4.20.0,+ build-depends: base ^>=4.13.0 || ^>=4.14.0 || ^>=4.15.0 || ^>=4.16.0 || ^>=4.17.0 || ^>=4.18.0 || ^>=4.19.0 || ^>=4.20.0 || ^>=4.21.0, hspec ^>=2.9.1 || ^>=2.10.6 || ^>=2.11.9, greskell, should-not-typecheck ^>=2.1.0@@ -104,7 +104,7 @@ other-modules: ServerTest.Common if flag(server-test) -- Explicitly remove dependency. See https://github.com/haskell/cabal/issues/1725- build-depends: base ^>=4.13.0 || ^>=4.14.0 || ^>=4.15.0 || ^>=4.16.0 || ^>=4.17.0 || ^>=4.18.0 || ^>=4.19.0 || ^>=4.20.0,+ build-depends: base ^>=4.13.0 || ^>=4.14.0 || ^>=4.15.0 || ^>=4.16.0 || ^>=4.17.0 || ^>=4.18.0 || ^>=4.19.0 || ^>=4.20.0 || ^>=4.21.0, aeson ^>=2.0.2 || ^>=2.1.0 || ^>=2.2.3, hspec ^>=2.9.1 || ^>=2.10.6 || ^>=2.11.9, text ^>=1.2.3 || ^>=2.0.2 || ^>=2.1,@@ -135,7 +135,7 @@ other-extensions: OverloadedStrings other-modules: ServerTest.Common if flag(server-behavior-test)- build-depends: base ^>=4.13.0 || ^>=4.14.0 || ^>=4.15.0 || ^>=4.16.0 || ^>=4.17.0 || ^>=4.18.0 || ^>=4.19.0 || ^>=4.20.0,+ build-depends: base ^>=4.13.0 || ^>=4.14.0 || ^>=4.15.0 || ^>=4.16.0 || ^>=4.17.0 || ^>=4.18.0 || ^>=4.19.0 || ^>=4.20.0 || ^>=4.21.0, aeson ^>=2.0.2 || ^>=2.1.0 || ^>=2.2.3, hspec ^>=2.9.1 || ^>=2.10.6 || ^>=2.11.9, text ^>=1.2.3 || ^>=2.0.2 || ^>=2.1,
src/Data/Greskell/Binder.hs view
@@ -88,7 +88,11 @@ -- Unsafely create a placeholder variable of arbitrary type with the -- given index. unsafePlaceHolder :: PlaceHolderIndex -> Greskell a-unsafePlaceHolder = unsafeGreskellLazy . toPlaceHolderVariable+unsafePlaceHolder = unsafeGreskellLazy . wrapWithParens . toPlaceHolderVariable+ where+ wrapWithParens v = "((" <> v <> "))"+ -- This is necessary to ensure the v is always treated as a varible name (NOT a type name) in Groovy script.+ -- See https://github.com/debug-ito/greskell/issues/18 -- | __This function is only for internal use.__ --
src/Data/Greskell/Extra.hs view
@@ -125,21 +125,21 @@ for_writePropertyKeyValues = let binder = (writePropertyKeyValues [("age", (21 :: Int))] :: Binder (Walk SideEffect AVertex AVertex)) (walk, binding) = runBinder binder- in [ (unpack $ toGremlin walk, "__.property(\"age\",__v0).identity()")+ in [ (unpack $ toGremlin walk, "__.property(\"age\",((__v0))).identity()") , (show $ sortBy (comparing fst) $ KeyMap.toList binding, "[(\"__v0\",Number 21.0)]") ] for_writeKeyValues = let keyAge = ("age" :: Key AVertex Int) keyName = ("name" :: Key AVertex Text) (walk, binding) = runBinder $ writeKeyValues <$> sequence [keyAge <=:> 21, keyName <=:> "Josh"]- in [ (unpack $ toGremlin walk, "__.property(\"age\",__v0).property(\"name\",__v1).identity()")+ in [ (unpack $ toGremlin walk, "__.property(\"age\",((__v0))).property(\"name\",((__v1))).identity()") , (show $ sortBy (comparing fst) $ KeyMap.toList binding, "[(\"__v0\",Number 21.0),(\"__v1\",String \"Josh\")]") ] for_operators = let keyNName = ("nickname" :: Key AVertex (Maybe Text)) keyCompany = ("company" :: Key AVertex (Maybe Text)) (walk, binding) = runBinder $ writeKeyValues <$> sequence [keyNName <=?> Nothing, keyCompany <=?> Just "foobar.com"]- in [ (unpack $ toGremlin walk, "__.property(\"company\",__v0).identity()")+ in [ (unpack $ toGremlin walk, "__.property(\"company\",((__v0))).identity()") , (show $ sortBy (comparing fst) $ KeyMap.toList binding, "[(\"__v0\",String \"foobar.com\")]") ] for_gWhenEmptyInput =
test/Data/Greskell/BinderSpec.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE OverloadedStrings #-} module Data.Greskell.BinderSpec ( main , spec@@ -8,7 +9,8 @@ import Data.Aeson (toJSON) import qualified Data.Aeson.Key as Key import qualified Data.Aeson.KeyMap as KM-import Data.Text (unpack)+import Data.Text (Text)+import qualified Data.Text as T import Test.Hspec import Data.Greskell.AsLabel (AsLabel)@@ -18,50 +20,57 @@ main :: IO () main = hspec spec -shouldBeVariable :: Greskell a -> IO ()-shouldBeVariable got_greskell =- case unpack $ toGremlin got_greskell of- [] -> expectationFailure "Expect a Gremlin variable, but got empty script."- (h : rest) -> do- h `shouldSatisfy` (`elem` variableHeads)- forM_ rest (`shouldSatisfy` (`elem` variableRests))+extractVarName :: Greskell a -> IO Text+extractVarName got_greskell = checkVarName =<< (stripParens $ toGremlin got_greskell) where+ stripParens v =+ case T.stripPrefix "((" =<< T.stripSuffix "))" v of+ Nothing -> do+ expectationFailure "Binder should produce an expression of a variable wrapped with double parens"+ return ""+ Just a -> return a+ checkVarName v =+ case T.unpack v of+ [] -> do+ expectationFailure "Expect a Gremlin variable, but got empty script."+ return ""+ (h : rest) -> do+ h `shouldSatisfy` (`elem` variableHeads)+ forM_ rest (`shouldSatisfy` (`elem` variableRests))+ return v variableHeads = '_' : (['a' .. 'z'] ++ ['A' .. 'Z']) variableRests = variableHeads ++ ['0' .. '9'] -toKey :: Greskell a -> Key.Key-toKey = Key.fromText . toGremlin- spec :: Spec spec = describe "Binder" $ do it "should keep bound values" $ do let b = do v1 <- newBind (100 :: Int)- v2 <- newBind "hogehoge"+ v2 <- newBind ("hogehoge" :: Text) return (v1, v2) ((got_v1, got_v2), got_bind) = runBinder b toGremlin got_v1 `shouldNotBe` toGremlin got_v2- shouldBeVariable got_v1- shouldBeVariable got_v2- got_bind `shouldBe` KM.fromList [ (toKey got_v1, toJSON (100 :: Int)),- (toKey got_v2, toJSON "hogehoge")+ v1Name <- extractVarName got_v1+ v2Name <- extractVarName got_v2+ got_bind `shouldBe` KM.fromList [ (Key.fromText v1Name, toJSON (100 :: Int)),+ (Key.fromText v2Name, toJSON ("hogehoge" :: Text)) ] it "should compose and produce new variables" $ do- let b = newBind "foobar"+ let b = newBind ("foobar" :: Text) ((got_v1, got_v2), got_bind) = runBinder $ ((,) <$> b <*> b) toGremlin got_v1 `shouldNotBe` toGremlin got_v2- shouldBeVariable got_v1- shouldBeVariable got_v2- got_bind `shouldBe` KM.fromList [ (toKey got_v1, toJSON "foobar"),- (toKey got_v2, toJSON "foobar")+ v1Name <- extractVarName got_v1+ v2Name <- extractVarName got_v2+ got_bind `shouldBe` KM.fromList [ (Key.fromText v1Name, toJSON ("foobar" :: Text)),+ (Key.fromText v2Name, toJSON ("foobar" :: Text)) ] it "should also be able to produce AsLabels" $ do let newIntLabel :: Binder (AsLabel Int) newIntLabel = newAsLabel- newVar = newBind "foobar"+ newVar = newBind ("foobar" :: Text) ((got_v1, got_l1, got_v2, got_l2), _) = runBinder $ ((,,,) <$> newVar <*> newIntLabel <*> newVar <*> newIntLabel)- shouldBeVariable got_v1- shouldBeVariable got_v2+ _ <- extractVarName got_v1+ _ <- extractVarName got_v2 toGremlin got_v1 `shouldNotBe` toGremlin got_v2 got_l1 `shouldNotBe` got_l2
test/Data/Greskell/ExtraSpec.hs view
@@ -35,15 +35,15 @@ let input :: [(Text, Int)] input = [("age", 24)] (runBoundWalk $ writePropertyKeyValues input)- `shouldBe` ( "__.property(\"age\",__v0).identity()",+ `shouldBe` ( "__.property(\"age\",((__v0))).identity()", KM.fromList [("__v0", Number 24)] ) specify "multiple props" $ do let input :: [(Text, Value)] input = [("age", Number 24), ("name", String "Toshio"), ("foo", String "bar")] (runBoundWalk $ writePropertyKeyValues input)- `shouldBe` ( "__.property(\"age\",__v0).property(\"name\",__v1)"- <> ".property(\"foo\",__v2).identity()",+ `shouldBe` ( "__.property(\"age\",((__v0))).property(\"name\",((__v1)))"+ <> ".property(\"foo\",((__v2))).identity()", KM.fromList [ ("__v0", Number 24), ("__v1", String "Toshio"), ("__v2", String "bar")
test/ServerTest.hs view
@@ -23,7 +23,7 @@ import Data.Greskell.AsIterator (AsIterator (IteratorItem)) import Data.Greskell.AsLabel (AsLabel (..), lookupAsM) import qualified Data.Greskell.AsLabel as As-import Data.Greskell.Binder (newBind, runBinder)+import Data.Greskell.Binder (Binder, newBind, runBinder) import Data.Greskell.Extra (gWhenEmptyInput) import Data.Greskell.GMap (GMapEntry, unGMapEntry) import Data.Greskell.Graph (AEdge (..), AProperty (..), AVertex (..),@@ -33,8 +33,8 @@ (=:)) import Data.Greskell.GraphSON (FromGraphSON, GValue, nonTypedGValue, parseEither)-import Data.Greskell.Gremlin (Order, P, Predicate (..), cCompare, oDecr,- oIncr, pAnd, pEq, pGte, pLt, pNot, pTest)+import Data.Greskell.Gremlin (Order, P, Predicate (..), cCompare, oAsc, oDesc,+ pAnd, pEq, pGte, pLt, pNot, pTest) import Data.Greskell.Greskell (Greskell, ToGreskell (..), false, gvalueInt, list, number, single, toGremlin, toGreskell, true, unsafeGreskell, unsafeMethodCall, value)@@ -60,6 +60,7 @@ spec :: Spec spec = withEnv $ do spec_basics+ spec_binder spec_comparator spec_predicate spec_T@@ -155,19 +156,34 @@ => Greskell a -> b -> SpecWith (String, Int) checkOne input expected = checkRaw input [expected] +checkBinder :: (AsIterator a, b ~ IteratorItem a, FromGraphSON b, Eq b, Show b)+ => [b] -> Binder (Greskell a) -> SpecWith (String, Int)+checkBinder expected binder = specify label $ withClient $ \client -> do+ got <- WS.slurpResults =<< WS.submit client script (Just binding)+ got `shouldBe` V.fromList expected+ where+ (script, binding) = runBinder binder+ label = (unpack $ toGremlin script) ++ " with " ++ show binding +spec_binder :: SpecWith (String, Int)+spec_binder = describe "server and Binder" $ do+ checkBinder [100 :: Int] $ newBind (100 :: Int)+ checkBinder [110 :: Int] $ do+ b <- newBind (100 :: Int)+ return $ b + (10 :: Greskell Int)+ spec_comparator :: SpecWith (String,Int) spec_comparator = do- let oIncr' :: Greskell (Order Int)- oIncr' = oIncr- oDecr' :: Greskell (Order Int)- oDecr' = oDecr- checkOne (cCompare oIncr' 20 20) 0- checkOne (cCompare oIncr' 10 20) (-1)- checkOne (cCompare oIncr' 20 10) 1- checkOne (cCompare oDecr' 20 20) 0- checkOne (cCompare oDecr' 10 20) 1- checkOne (cCompare oDecr' 20 10) (-1)+ let oAsc' :: Greskell (Order Int)+ oAsc' = oAsc+ oDesc' :: Greskell (Order Int)+ oDesc' = oDesc+ checkOne (cCompare oAsc' 20 20) 0+ checkOne (cCompare oAsc' 10 20) (-1)+ checkOne (cCompare oAsc' 20 10) 1+ checkOne (cCompare oDesc' 20 20) 0+ checkOne (cCompare oDesc' 10 20) 1+ checkOne (cCompare oDesc' 20 10) (-1) spec_predicate :: SpecWith (String,Int) spec_predicate = do