{"id":13807248,"url":"https://github.com/aesteve/grooveex","last_synced_at":"2025-07-06T12:34:02.298Z","repository":{"id":87789526,"uuid":"47360377","full_name":"aesteve/grooveex","owner":"aesteve","description":"Extension module for vertx-groovy adding methods and syntaxic sugar","archived":false,"fork":false,"pushed_at":"2016-05-23T19:35:03.000Z","size":305,"stargazers_count":14,"open_issues_count":6,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-05-06T22:58:22.146Z","etag":null,"topics":["async","dsl","groovy","vertx","vertx-web"],"latest_commit_sha":null,"homepage":null,"language":"Groovy","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/aesteve.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2015-12-03T21:04:47.000Z","updated_at":"2020-06-08T16:39:51.000Z","dependencies_parsed_at":"2024-01-03T02:25:12.583Z","dependency_job_id":"ba056515-a36d-472c-8961-d8580594f5c4","html_url":"https://github.com/aesteve/grooveex","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/aesteve/grooveex","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aesteve%2Fgrooveex","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aesteve%2Fgrooveex/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aesteve%2Fgrooveex/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aesteve%2Fgrooveex/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/aesteve","download_url":"https://codeload.github.com/aesteve/grooveex/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aesteve%2Fgrooveex/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":263901085,"owners_count":23527359,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["async","dsl","groovy","vertx","vertx-web"],"created_at":"2024-08-04T01:01:23.149Z","updated_at":"2025-07-06T12:34:02.279Z","avatar_url":"https://github.com/aesteve.png","language":"Groovy","funding_links":[],"categories":["Language Support"],"sub_categories":[],"readme":"## Add syntaxic sugar on top of vertx-lang-groovy\n\nIf you want to use Vert.x in Groovy with a less idiomatic but more groovy-ish API !\n\nHave a look at [the tests](/src/test/groovy/com/github/aesteve/vertx/groovy/specs) if you want to have a glimpse at how the sugar looks in action.\n\nEnjoy as part of a healthy diet :)\n\n## Adding extensions\n\nSince the projects relies on an Groovy extension module, you just have to add the jar to your project. Nothing more.\n\nExample, if you're using Gradle: \n```groovy\nrepositories {\n\tjcenter()\n}\n\ndependencies {\n\tcompile 'com.github.aesteve:grooveex:0.5'\n}\n```\n\nAnd you're done. The new methods will also work with `@TypeChecked` since it's an extension module, not a metaclass.\n\n## Examples\n\n### Basic router, dealing with request / responses\n\n```groovy\nVertx vertx = Vertx.vertx\n\n// Server-side\nRouter router = vertx.router\nrouter['/hello'] \u003e\u003e { // router.get('hello').handler {\n  def name = request.params['name'] // params is available through getParams =\u003e as a attribute\n  response \u003c\u003c \"Hello $name\"  // it.response().end(\"Hello $name\"), routingContext is implicit, thus response is it.response()\n}\nserver.requestHandler router.\u0026accept\nvertx.createHttpServer().listen()\n\n// Client side\nHttpClient client = vertx.createHttpClient()\nHttpRequest req = client[\"/hello?name=World\"] // client.get \"/hello?name=World\"\nreq \u003e\u003e { response -\u003e // req.handler {\n  response \u003e\u003e\u003e { // response.bodyHandler\n    assert it as String == 'Hello World' // as String =\u003e buffer.toString 'UTF-8'\n  } \n} \nreq++ // req.end()\n```\n\n### Dealing with async results / futures\n\n```groovy\nvertx.executeBlocking({ future -\u003e\n  try {\n    sleep 1000\n    future += 'completed !' // plus operator means complete('...'), plusplus operator means complete() \n  } catch(all) {\n    future -= 'interrupted :(' // minus operator means fail\n  }\n}, { asyncResult -\u003e\n  if (asyncResult) { // asBoolean method overloading =\u003e you can use it in an if statement to check if succeeded() \n    println 'It has succeeded !'\n  }\n  boolean successful = +asyncResult \u0026\u0026 asyncResult.result == 'completed' // +asyncResult is asyncResult.succeeded() and getResult() is defined \n  boolean unsuccessful = -asyncResult || !asyncResult.result == 'completed' // -asyncResult is asyncResult.failed() and getResult() is defined\n})\n\n```\n\n\n### Pumping streams\n\n```groovy\nVertx vertx = Vertx.vertx\n\n// Server-side\nRouter router = vertx.router\nrouter['/pump'] \u003e\u003e {\n  Buffer received = Buffer.buffer()\n  request \u003e\u003e { buff -\u003e // request handler\n    received += buff // appends the received buffer (received \u003c\u003c buff also works) \n  }\n}\nserver.requestHandler router.\u0026accept\nvertx.createHttpServer().listen()\n\n// Client side (sends a file to the server)\nHttpClient client = vertx.createHttpClient()\nHttpRequest req = client[\"/pump\"]\nvertx.fileSystem.open 'test-file', [:], { res -\u003e\n  AsyncFile file = res.result\n  Pump filePump = file | req // createPump between readstream and writestream\n  filePump++\n}\n```\n\n### Dealing with the event bus\n\n```groovy\nVertx vertx = Vertx.vertx\nEventBus eb = vertx.eventBus\nBuffer msg = 'Hello !' as Buffer // Buffer.buffer('Hello !')\neb['some-address'] \u003e\u003e { message -\u003e // eb.consumer('some-address', { ... \n  println \"Received : ${message.body}\" // message.body()\n}\neb['some-address'] \u003c\u003c 'Hello !' // eb.send('some-address', 'Hello !')\n```\n\n### Invoking an async service\n```groovy\nrouter.get '/async' \u003e\u003e { context -\u003e\n  invokeAsyncMethod request.params['something'], fail | { result -\u003e\n    response \u003c\u003c result\n  }\n}\n```\nEquivalent to : \n```groovy\nrouter.get('/async').handler { context -\u003e\n  invokeAsyncMethod context.request().params['something'], { res -\u003e\n    if (res.failed()) {\n      context.fail(res.cause())\n    } else {\n      response \u003c\u003c res.result()\n    }\n  }\n}\n```\n\n\n## Rule of thumb\n\nBasically : \n\n* `a + b` means either `a.append(b)`, `a.write(b)` or `a.complete(b)`\n* `a - b` means `a.fail(b)`\n* `a++` means `a.complete()` or `a.next()`\n* `a \u003c\u003c b` means `a.end(b)`\n* `a \u003e\u003e b` means `a.handler(b)`\n* `a \u003e\u003e\u003e b` means `a.bodyHandler(b)` (a \"global\" handler)\n* `a | b` means `a.pipe(b)` (pumps)\n* `+a` means `a.succeeeded()` `a.completed()`\n* `-a` means `a.failed()`\n\nEvery getter method like `a.response()`, `a.request()` will also be available as an object attribute `a.response`, `a.request` (through `a.getResponse()` - Groovy convention -).  \n\n\n\n\n## RouterBuilder\n\nOn top of the syntaxic sugar, you can use the `RouterBuilder` class to create a vertx-web router from a closure.\n\nJust have a look at the [routing file example](blob/master/src/test/resources/routes.groovy) to see how it looks like.\nIt makes use of the overloaded operators like `\u003c\u003c` or `\u003e\u003e` but also \"wraps\" Vert.x's handler closure to inject `RoutingContext` as delegate, so that you can directly write `response.headers` for instance and not `it.response().headers`.\nEvery method available in `RoutingContext` will be directly available within your closure.\n\nKeep in mind that the DSL doesn't force you to write all your handlers within the routingFile.\n\nVert.x defines a Groovy Handler (middleware) as either a class implementing the `Handler` interface, a closure or a method reference.\n\nThat means you can do the following :\n\n```groovy\nRouterBuidler.make {\n\tget('/accessAnObject').handler(new MyHandler())\n\tget('/accessAMethod').handler(new HandlerWithMultipleMethods().\u0026someMethod)\n\tget('/accessAClosure', SomeClass.aStaticClosure)\n\tget('/inline') {\n\t\tresponse.end \u003c\u003c \"or just inline stuff if it's simple enough\" \n\t}\n}\n```\n\nAnd since you receive the `Router` instance once its built, you can use it programmatically as you're used to with Vert.x ! \n\n### Split routing rules across multiple files\n\nAs soon as your application grows, you'll feel the need to separate concerns and the routing concerns separate.\nFor instance the part dealing with static resources, server-side templates, could be kept together, whereas middlewares for REST-APIs should be kept in a separate file. \n\n```groovy\nRouterBuilder builder = new RouterBuilder(vertx: vertx)\nFile routingFolder = new File('/some/path/routing_foler')\nRouter router = builder(routingFolder.listFiles().collect { it.name.endsWith('.groovy') })\n```\n\n### Nesting routes\n\n```groovy\nRouterBuilder builder = new RouterBuilder() \nRouter router = builder {\n  route('/blood') {\n    // ...\n    route('/sugar') {\n      get {\n        response \u003c\u003c 'Yes please !'\n      }\n      post {\n        String sent = body as String\n        if (sent == 'I want that sugar sweet') {\n          response \u003c\u003c \"Don't let nobody touch it\"\n        } else {\n          fail 400\n        }\n      }\n      route('/sex') {\n        route('/magic') {\n          blocking = true\n          cors '*'\n          get {\n            response.headers['X-Song'] = 'Under the bridge'\n            response.headers['X-Artist'] = 'RHCP'\n            response.headers[HttpHeaders.DATE] = \"Tue, 10 Mar 1992 12:45:26 GMT\"\n            response.chunked = true\n            response += 'Is the city I live in...'\n            sleep 1000\n            response += '...The city of Angels'\n            sleep 1000\n            response \u003c\u003c '- RHCP (1991)'\n          }\n        }\n      }\n    }\n  }\n}\n```\n\n### Check/Expect\n\n```groovy\nroute('/expect') { // fails with 400, even if an exception is thrown (be careful !)\n\texpect { params['exists'] }\n\texpect { Long.valueOf params['long'] } // can throw NumberFormatException -\u003e but 400 anyway\n\texpect { headers[AUTHORIZATION]?.indexOf('token ') == 0 }\n\tget {\n\t\tresponse \u003c\u003c \"everything's fine\"\n\t}\n}\nroute('/check') { // fails with the specified statusCode, and doesn't swallow exceptions\n\tcheck { params['token'] } | 401\n\tcheck { params['token'] == 'magic' } | 403\n\tget {\n\t\tresponse \u003c\u003c \"everything's fine\"\n\t}\n}\n```\n\n### Payload \u0026 Marshalling\n\nYou can set a marshaller to read request body or write response body automatically simply by implementing the `com.github.aesteve.vertx.groovy.io.Marshaller` interface.\nBy default, since Vert.x already uses Jackson, you can use the `JacksonMarshaller` we provided.\n\nThen, just declare that your router (or route), uses this marshaller:\n\n```groovy\nrouter {\n    marshaller 'application/json', new JacksonMarshaller()\n    // ...\n}\n```\n\n#### Read request body using marshaller\n\n```groovy\nrouter {\n    marshaller 'application/json', new JacksonMarshaller()\n    post('/create') {\n        def marshalledBody = body as SomeObject // using 'as' on body will automatically invoke the marshaller\n        // ...\n    }\n}\n```\n\n#### Write response body\n\n```groovy\nrouter {\n    marshaller 'application/json', new JacksonMarshaller()\n    post('/create') {\n        def marshalledBody = body as SomeObject\n        def something = marshalledBody.transform() // do some stuff\n        yield something // when you yield an object, it's set as response payload and calls context.next(), it will be marshalled automatically\n    }\n}\n```\n\n#### Register your own DSL item\n\nObviously we can't provide you a full list of useful middlewares, handlers. Moreover, you'll probably need a lot of stuff specific to your application. \n\nSince it's pretty cool to read a routing file in a declarative way (like `expect {something}` for instance), you can register your own handlers/middlewares against RouterBuilder:\n```groovy\nRouterBuilder builder = new RouterBuilder()\nbuilder.extensions['injectHeader'] = { String headerName, String headerValue -\u003e\n\treturn { RoutingContext ctx -\u003e\n\t\tresponse.headers[headerName] = headerValue\n\t\tctx++\n\t}\n}\nbuilder {\n\troute('/dateHeader') {\n\t\tinjectHeader 'Date', \"${-\u003e new Date() }\" // lazy eval\n\t\tget {\n\t\t\tresponse \u003c\u003c 'You should have the date header yay !\"\n\t\t}\n\t}\n}\n```\n\nOr if you prefer putting it into the routing file directly:\n```groovy\nrouter {\n\textension('injectHeader') { String headerName, String headerValue -\u003e\n\t\treturn { RoutingContext ctx -\u003e\n\t\t\tresponse.headers[headerName] = headerValue\n\t\t\tctx++\n\t\t}\n\t}\n\troute('/dateHeader') {\n\t\tinjectHeader 'Date', \"${-\u003e new Date() }\" // lazy eval\n\t\tget {\n\t\t\tresponse \u003c\u003c 'You should have the date header yay !\"\n\t\t}\n\t}\n}\n```\n\n#### Matching multiple http methods\n\nYou can use either `|`, `\u0026` or `/` operator to match multiple http methods. Just use the one that suits you the most.\n\n```groovy\nrouter {\n  route('/multi') {\n    get | post {\n      response \u003c\u003c 'get or post'\n    }\n    options \u0026 delete {\n      response \u003c\u003c 'options or delete'\n    }\n    trace / connect {\n      response \u003c\u003c 'trace or connect'\n    }\n  }\n}\n```\n\n#### Matchin every (declared) http methods\n\nIn case you want to match all the http methods you've (or will) declare in your route, you can use the keyword `all`.\n\n```groovy\nrouter {\n  route('/all') {\n    all {\n      response.chunked = true\n      it++\n    }\n    get {\n      response + 'get '\n      it++\n    }\n    post {\n      response + 'post '\n      it++\n    }\n    all {\n      response \u003c\u003c 'Done'\n    }\n  }\n}\n```\n\n## Complete list of syntaxic sugar\n\n### WriteStream\n\nExamples : `ServerWebSocket`, `SockJSSocket`, `HttpClientRequest`, `HttpServerResponse`\n\n| Groovy sugar  | Vert.x standard |\n| ------------- | --------------- |\n| `stream += data` | `stream.write(data)` |\n| `stream \u003c\u003c data` | `stream.end(data)` |\n\n\n### ReadStream\n\nExamples : `WebSocket`, `HttpServerRequest`, ...\n\n| Groovy sugar  | Vert.x standard |\n| ------------- | --------------- |\n| `stream \u003e\u003e handler` | `stream.handler(handler)` |\n| `stream \u003e\u003e\u003e handler` | `stream.endHandler(handler)` |\n| `stream | other` | `Pump.pump(stream, other)` |\n\n\n### Route\n\n| Groovy sugar  | Vert.x standard |\n| ------------- | --------------- |\n| `route \u003e\u003e handler` | `route.handler(handler)` |\n| `route ~/somePattern/` | `route.pathRegex(\"somePattern\")` |\n\n### Router\n\n| Groovy sugar  | Vert.x standard |\n| ------------- | --------------- |\n| `router['route'] = handler` | `router.route('route').handler(handler)` |\n\n### Buffer\n\n| Groovy sugar  | Vert.x standard |\n| ------------- | --------------- |\n| `buff \u003c\u003c other` | `buffer.appendBuffer(other)` |\n| `buff \u003c\u003c 'something'` | `buffer.appendString('something')` |\n| `buff \u003c\u003c \"hello $name\"` | `buffer.appendString(\"hello $name\".toString()\")` |\n| `buff += other` | `buffer.appendBuffer(other)` |\n| `buff += 'something'` | `buffer.appendString('something')` |\n| `buff += \"hello $name\"` | `buffer.appendString(\"hello $name\".toString()\")` |\n| `buff as String` | `buffer.toString('UTF-8')` |\n\n### RoutingContext\n\n| Groovy sugar  | Vert.x standard |\n| ------------- | --------------- |\n| `ctx['key']` | `ctx.get('key')` |\n| `ctx['key'] = value` | `ctx.put('key', value)` |\n| `ctx.cookies` | `ctx.cookies()` |\n| `ctx.mountPoint` | `ctx.mountPoint()` |\n| `ctx.normalisedPath` | `ctx.normalisedPath()` |\n| `ctx.response` | `ctx.response()` |\n| `ctx.request` | `ctx.request()` |\n| `ctx.session` | `ctx.session()` |\n| `ctx.statusCode` | `ctx.statusCode()` |\n| `ctx.redirect 'somewhere', statusCode` | `ctx.response().putHeader(HttpHeaders.LOCATION, 'somewhere').setStatusCode(statusCode).end()` |\n| `ctx.user` | `ctx.user()` |\n| `ctx.vertx` | `ctx.vertx()` |\n| `ctx - 400` | `ctx.fail(400)` |\n| `ctx - new RuntimeException()` | `ctx.fail(new RuntimeException())` |\n| `ctx++` | `ctx.next()` |\n| `ctx \u003e\u003e closure` | `{ res -\u003e if (res.failed) { ctx.fail(res.cause()) } else { closure(res.result()) } }` |\n\n#### Notes\n\nNB : you already can call `ctx++` (without this lib) since the method on `RoutingContext` is already called `next()`\n\nNB : the last method is a very common pattern when you invoke an async method that takes an Handler\u003cAsyncResult\u003e as parameter. If it fails, you just want the context to fail, else, you'll need the result to do something with.\n\n#### Additional methods\n\nYou'll find an `ensure` method within `RoutingContext` which is very useful for dealing with a common pattern in Vert.x.\n\nOften, one of the first handlers in your routes will invoke an async service and :\n\n* fail if the service invocation failed ( 500 )\n* check the result asynchronously, then :\n    * fail with some statusCode if the result doesn't suit you (say, the token is invalid)\n    * store the result somewhere, or do something with the result if the results suits you, then call `context.next()`\n\nIn action :\n\n```groovy\nrouter.get('/api/1/*').handler { ctx -\u003e\n    accessTokenChecker.check(ctx.request().params().get('accessToken'), {\n        if (res.failed()) {\n            ctx.fail res.cause()\n        } else {\n            User user = res.result()\n            if (user \u0026\u0026 user.validated) {\n                ctx.setUser(user)\n                ctx.next()\n            } else {\n                ctx.fail 401\n            }\n        }\n    })\n}\n```\n\nHere's the `ensure` equivalent (buckle up !)\n\n```groovy\nrouter.get('/api/1/').handler { ctx -\u003e\n    accessTokenChecker.check(ctx.request().params().get('accessToken'), ctx.ensure({ it \u0026\u0026 it.validated }) \u0026 { user = it } | 401)\n}\n```\n\nNow let's benefit of full sugar and rewrite it :\n```groovy\nrouter.get('/api/1/') \u003e\u003e {\n    def accessToken = params['accessToken']\n    def putTokenIfExistsOrFailWith401 = ensure({ it \u0026\u0026 it.validated }) \u0026 { user = it } | 401\n    accessTokenChecker.check accessToken, putTokenIfExistsOrFailWith401 \n}\n```\n\nThis is especially useful for storing and composing small pieces of reusable logic into closures, like so :\n\n```groovy\ndef userHasValidAccount = { user -\u003e\n    user \u0026\u0026 user.validated\n}\nrouter.get('/api/1/') \u003e\u003e {\n    def accessToken = params['accessToken']\n    def putTokenIfExistsOrFailWith401 = ensure(userHasValidAccount) \u0026 { user = it } | 401\n    accessTokenChecker.check accessToken, putTokenIfExistsOrFailWith401 \n}\n```\n\n### HttpServerRequest\n\n| Groovy sugar  | Vert.x standard |\n| ------------- | --------------- |\n| `req.params` | `req.params()` |\n| `req.headers` | `req.headers()` |\n| `req.method` | `req.method()` |\n| `req.path` | `req.path()` |\n| `req.remoteAddress` | `req.remoteAddress()` |\n| `req - '/path'` | `req.path() - '/path'` |\n| `req \u003e\u003e\u003e handler` | `req.bodyHandler(handler)` |\n\n### HttpServerResponse\n\n| Groovy sugar  | Vert.x standard |\n| ------------- | --------------- |\n| `resp.headers` | `resp.headers()` |\n| `resp.headers = headers` | `resp.headers().clear().addAll(headers)` |\n| `resp++` | `resp.end()` |\n| `resp \u003c\u003c new JsonBuilder(...)` | `resp.end(new JsonBuilder(...).toString())` |\n\n### HttpClient\n\n| Groovy sugar  | Vert.x standard |\n| ------------- | --------------- |\n| `client['/path']` | `client.get('/path')` |\n\n\n### HttpClientRequest\n\n| Groovy sugar  | Vert.x standard |\n| ------------- | --------------- |\n| `req++` | `req.end()` |\n| `req \u003c\u003c new JsonBuilder(...)` | `req.end(new JsonBuilder(...).toString())` |\n| `req.method` | `req.method()` |\n| `req.headers = headers` | `req.headers().clear().addAll(headers)` |\n\n### HttpClientResponse\n\n| Groovy sugar  | Vert.x standard |\n| ------------- | --------------- |\n| `resp \u003e\u003e\u003e handler` | `resp.bodyHandler(handler)` |\n| `resp.headers` | `resp.headers()` |\n\n### Message\n\n| Groovy sugar  | Vert.x standard |\n| ------------- | --------------- |\n| `msg.address` | `msg.address()` |\n| `msg.replyAddress` | `msg.replyAddress()` |\n| `msg.headers` | `msg.headers()` |\n| `msg.body` | `msg.body()` |\n| `msg \u003c\u003c 'reply'` | `msg.reply('reply')` |\n\n### AsyncResult\n\n| Groovy sugar  | Vert.x standard |\n| ------------- | --------------- |\n| `ar.failed` | `ar.failed()` |\n| `-ar` | `ar.failed()` |\n| `ar.succeeded` | `ar.succeeded()` |\n| `+ar` | `ar.succeeded()` |\n| `ar.result` | `ar.result()` |\n| `ar.cause` | `ar.cause()` |\n| `if (ar) {` | `if (ar?.succeeded()) {` |\n\n\n### Future\n\n| Groovy sugar  | Vert.x standard |\n| ------------- | --------------- |\n| `f += result` | `f.complete(result)` |\n| `f -= cause` | `f.fail(cause)` |\n| `f + result` | `f.complete(result)` |\n| `f - cause` | `f.fail(cause)` |\n| `f++` | `f.complete()` |\n\n\n### Pump\n| Groovy sugar  | Vert.x standard |\n| ------------- | --------------- |\n| `pump++` | `pump.start()` |\n| `pump--` | `pump.stop()` |\n\n\n### EventBus\n\n| Groovy sugar  | Vert.x standard |\n| ------------- | --------------- |\n| `eb['address'] \u003e\u003e handler` | `eb.consumer('address', handler)` |\n| `eb['address'] \u003c\u003c msg` | `eb.send(address, msg)` |\n| `eb['address'] ** msg` | `eb.publish(address, msg)` |\n\n### Vertx\n| Groovy sugar  | Vert.x standard |\n| ------------- | --------------- |\n| `Vertx.vertx` | `Vertx.vertx()` |\n| `vertx.eventBus` | `vertx.eventBus()` |\n| `vertx.fileSystem` | `vertx.fileSystem()` |\n| `vertx.router` | `Router.router(vertx)` |\n\n### String\n\n| Groovy sugar  | Vert.x standard |\n| ------------- | --------------- |\n| `'something' as Buffer` | `Buffer.buffer('something')` |\n\n### JsonBuilder\n\n| Groovy sugar  | Vert.x standard |\n| ------------- | --------------- |\n| `new JsonBuilder(...) as Buffer` | `Buffer.buffer(new JsonBuilder(...).toString())` |\n\n### Context\n\n| Groovy sugar  | Vert.x standard |\n| ------------- | --------------- |\n| `context.config` | `context.config()` |\n\n### MultiMap\n\n| Groovy sugar  | Vert.x standard |\n| ------------- | --------------- |\n| `map[key]` | `map.get(key)` |\n| `map[key] = value` | `map.put(key, value)` |\n| `map -= key` | `map.remove(key)` |\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faesteve%2Fgrooveex","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Faesteve%2Fgrooveex","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faesteve%2Fgrooveex/lists"}