{"id":18384868,"url":"https://github.com/allegro/opel","last_synced_at":"2025-08-01T00:02:39.681Z","repository":{"id":10776672,"uuid":"66944787","full_name":"allegro/opel","owner":"allegro","description":"OPEL - asynchronous expression language","archived":false,"fork":false,"pushed_at":"2024-06-28T09:59:19.000Z","size":612,"stargazers_count":70,"open_issues_count":2,"forks_count":18,"subscribers_count":8,"default_branch":"main","last_synced_at":"2024-12-25T03:07:45.876Z","etag":null,"topics":["async","expression","hacktoberfest","java","language"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/allegro.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2016-08-30T13:49:35.000Z","updated_at":"2024-11-04T12:08:21.000Z","dependencies_parsed_at":"2024-06-27T22:30:43.344Z","dependency_job_id":"a7c7875b-3082-4947-8cd1-e34d0e05753e","html_url":"https://github.com/allegro/opel","commit_stats":null,"previous_names":[],"tags_count":25,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/allegro%2Fopel","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/allegro%2Fopel/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/allegro%2Fopel/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/allegro%2Fopel/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/allegro","download_url":"https://codeload.github.com/allegro/opel/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":232049596,"owners_count":18465275,"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","expression","hacktoberfest","java","language"],"created_at":"2024-11-06T01:15:44.863Z","updated_at":"2025-08-01T00:02:39.421Z","avatar_url":"https://github.com/allegro.png","language":"Java","funding_links":[],"categories":["并发编程"],"sub_categories":[],"readme":"[![Build](https://github.com/allegro/opel/actions/workflows/ci.yml/badge.svg)](https://github.com/allegro/opel/actions/workflows/ci.yml)\n[![Codecov](https://img.shields.io/codecov/c/github/allegro/opel.svg?style=flat)](https://codecov.io/gh/allegro/opel)\n[![GitHub Release Date](https://img.shields.io/github/release-date/allegro/opel.svg?style=flat)](https://github.com/allegro/opel/releases)\n[![GitHub license](https://img.shields.io/badge/license-Apache%20License%202.0-blue.svg?style=flat)](http://www.apache.org/licenses/LICENSE-2.0)\n\n# opel - asynchronous expression language\n\nopel was designed to let you write simple, short asynchronous expressions. It uses \n[Parboiled](https://github.com/sirthias/parboiled) as a language grammar engine and common Java 8 \n[CompletableFuture](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html).\n\nFor example `temperature()` function asks REST service for temperature in Fahrenheit for given capital city\nand we want to convert it to Celsius. We can write a simple expression to achievie it.\n\n```\n(temperature('Warsaw') - 32) * 5 / 9\n```\n\nthis expression will be transformed to equivalent code:\n\n```\ntemperature('Warsaw')\n\t.thenCombine(CompletableFuture.completedFuture(32), (l, r) -\u003e l - r)\n\t.thenCombine(CompletableFuture.completedFuture(5), (l, r) -\u003e l * r)\n\t.thenCombine(CompletableFuture.completedFuture(9), (l, r) -\u003e l / r)\n```\n\n## Contents\n* [Our business case](#our-business-case)\n* [Why have we created another language?](#why-have-we-created-another-language)\n* [What can opel do for you?](#what-can-opel-do-for-you)\n* [What opel can't do?](#what-opel-cant-do)\n* [Using with Gradle](#using-with-gradle)\n\n## \u003ca name=\"our-business-case\"\u003e\u003c/a\u003eOur business case\n\nIn [OpBox](http://allegro.tech/2016/03/Managing-Frontend-in-the-microservices-architecture.html)\nwhich is our solution to build frontend in microservices world we have to prepare site title depending on data returned \nfrom data source (REST service). For show product page it may look like:\n\n```\nrestService('showProduct') + ' - Allegro.pl - Więcej niż aukcje.'\n```\n\n## Why have we created another language?\n\nAllegro is a big platform with huge traffic. We encounter many performance issues so we decided \nto design everything asynchronously. Whenever it's possible we use CompletableFutures. It's a bit more complicated that way\nbut opel abstraction layer enables our users (non developers) to write simple expressions in easy way.\n\nAfter checking some expression languages like: \n[SpEL](http://docs.spring.io/spring/docs/current/spring-framework-reference/html/expressions.html) or \n[JEXL](http://commons.apache.org/proper/commons-jexl/) we didn't find any libraries supporting required asynchronous behavior.\n\n## What opel can't do?\n\nopel aims at very simple expressions. Certainly it won't be enough to make complicated scripts - but we want opel to stay that way.\n\n## What can opel do for you?\n\nopel supports:\n\n- primary math and string operations (i.e. `2+2*2`, `'Hello' + \"world !\"`)\n- escaping characters in strings (i.e. `'Hello, \\'World\\'!'`)\n- relational and equality operators (i.e. `2 == 3`, `2 \u003e 1 != false`)\n- logic operators (i.e. `true \u0026\u0026 false`, `false || true`)\n- simple map element access (i.e. `map.field` or `map['field']`)\n- simple list element access (i.e. `list[index]`)\n- object method calls (i.e. `'Hello, World!'.length()`)\n- if expressions (i.e. `if (2 \u003e 3) 'a' else 'b'`)\n- defining local constant values (i.e. `val x = 2+2*2; x * x`)\n- defining maps (i.e. `val x = {\"a\": \"b\"}; x.a`)\n- defining lists (i.e. `val x = ['a', 'b']; x[0]`)\n- defining the functions and lambda expression (i.e. `val x = a -\u003e a * a; val y = b -\u003e b + b; x(y(r))`)\n- registrable constant values (i.e. `'Hello, ' + WORLD_VALUE`)\n- registrable functions (i.e. `myFunction('Hello, World!')`)\n- registrable implicit conversions (i.e. `2 + '2'` or `'Hello, World!'.myMethod()`)\n\nMore can be found in [documentation](https://github.com/allegro/opel/wiki).\n\n## Using with Gradle\n\nBasically, all you have to do is to add a compile dependency:\n\n```\ndependencies {\n    compile 'pl.allegro.tech:opel:1.1.8'\n}\n```\n\n## Java usage examples\n\n### Evaluate simple expression\n\nCreate an instance of `OpelEngine` and evaluate the expression:\n\n```\nOpelEngine engine = OpelEngineBuilder.create()\n        .build();\n\nengine.eval(\"2 + 3\")\n        .whenComplete((result, error) -\u003e System.out.println(result));\n```\n\n### Evaluate expression with global variable\n\nCreate an instance of `OpelEngine` with global a variable and evaluate the expression:\n\n```\nOpelEngine engine = OpelEngineBuilder.create()\n        .withCompletedValue(\"PI\", 3.14)\n        .build();\n\nengine.eval(\"PI * 2\")\n        .whenComplete((result, error) -\u003e System.out.println(result));\n```\n\nNotice that in opel, all variables are final.\n\n### Evaluate expression with context variable\n\nThe engine is a heavy object and should be reused to evaluate different expressions.\nTo achieve this, variables can be provided in the context:\n\n```\nOpelEngine engine = OpelEngineBuilder.create()\n        .withCompletedValue(\"PI\", 3.14)\n        .build();\n\nString expression = \"PI * r * r\";\n\nEvalContext context = EvalContextBuilder.create()\n        .withCompletedValue(\"r\", 3)\n        .build();\n\nengine.eval(expression, context)\n        .whenComplete((result, error) -\u003e System.out.println(result));\n```\n\nIn the engine, you can configure general language for the application. \nIn context, you can provide, for example, request context like authorized username.\n\n### Evaluate expression with engine/context function\n\nFunctions in opel are implemented by `OpelAsyncFunction` interface and can be added as regular variable:\n\n```\nOpelAsyncFunction\u003cObject\u003e function = new OpelAsyncFunction\u003cObject\u003e() {\n            @Override\n            CompletableFuture\u003cObject\u003e apply(List\u003cCompletableFuture\u003c?\u003e\u003e args) {\n                Object result = // a call to an external service, to a database or other logic\n                return result;\n            }\n        };\n\nOpelEngine engine = OpelEngineBuilder.create()\n        .withCompletedValue(\"myFun\", function)\n        .build();\n\nString expression = \"myFun(a) * myFun(b)\";\n\nEvalContext context = EvalContextBuilder.create()\n        .withCompletedValue(\"a\", \"john\")\n        .withCompletedValue(\"b\", \"jenny\")\n        .build();\n\nengine.eval(expression, context)\n        .whenComplete((result, error) -\u003e System.out.println(result));\n```\n\nIn the same way, we add function to `OpelEngine` by `withCompletedValue` method, it can be added to `EvalContext`.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fallegro%2Fopel","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fallegro%2Fopel","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fallegro%2Fopel/lists"}