{"id":32181744,"url":"https://github.com/rsucasasf/cloj-rules-engine","last_synced_at":"2026-02-21T03:31:54.694Z","repository":{"id":62431595,"uuid":"92281481","full_name":"rsucasasf/cloj-rules-engine","owner":"rsucasasf","description":"Rules engine written in Clojure","archived":false,"fork":false,"pushed_at":"2017-06-28T11:45:50.000Z","size":125,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2025-10-21T22:50:52.862Z","etag":null,"topics":["clojure","java-interop","rules-engine"],"latest_commit_sha":null,"homepage":null,"language":"Clojure","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"epl-1.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rsucasasf.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-05-24T10:43:56.000Z","updated_at":"2024-05-15T00:40:52.000Z","dependencies_parsed_at":"2022-11-01T21:00:36.910Z","dependency_job_id":null,"html_url":"https://github.com/rsucasasf/cloj-rules-engine","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/rsucasasf/cloj-rules-engine","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rsucasasf%2Fcloj-rules-engine","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rsucasasf%2Fcloj-rules-engine/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rsucasasf%2Fcloj-rules-engine/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rsucasasf%2Fcloj-rules-engine/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rsucasasf","download_url":"https://codeload.github.com/rsucasasf/cloj-rules-engine/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rsucasasf%2Fcloj-rules-engine/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29672704,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-21T03:11:15.450Z","status":"ssl_error","status_checked_at":"2026-02-21T03:10:34.920Z","response_time":107,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["clojure","java-interop","rules-engine"],"created_at":"2025-10-21T22:50:51.236Z","updated_at":"2026-02-21T03:31:54.682Z","avatar_url":"https://github.com/rsucasasf.png","language":"Clojure","funding_links":[],"categories":[],"sub_categories":[],"readme":"# cloj-rules-engine\n\n[![Build Status](https://travis-ci.org/rsucasasf/cloj-rules-engine.svg?branch=master)](https://travis-ci.org/rsucasasf/cloj-rules-engine)\n[![License](https://img.shields.io/badge/License-EPL%201.0-red.svg)](https://opensource.org/licenses/EPL-1.0)\n[![GitHub release](https://img.shields.io/badge/version-0.1.2--SNAPSHOT-yellowgreen.svg)](https://github.com/rsucasasf/cloj-rules-engine)\n[![Codecov](https://img.shields.io/codecov/c/github/rsucasasf/cloj-rules-engine.svg)](https://codecov.io/gh/rsucasasf/cloj-rules-engine)\n[![Clojars Project](https://img.shields.io/clojars/v/clojars.org/cloj-rules-engine.svg)](https://clojars.org/clojars.org/cloj-rules-engine)\n\n**cloj-rules-engine** is a very simple rules engine written in [Clojure](https://clojure.org/) and designed to work with Java.\n\n- Maven:\n\n*pom.xml*:\n\n```xml\n\u003crepositories\u003e\n\t\u003crepository\u003e\n\t\t\u003cid\u003eclojars\u003c/id\u003e\n\t\t\u003curl\u003ehttp://clojars.org/repo/\u003c/url\u003e\n\t\u003c/repository\u003e\n\u003c/repositories\u003e\n```\n\n```xml\n\u003cdependency\u003e\n  \u003cgroupId\u003eclojars.org\u003c/groupId\u003e\n  \u003cartifactId\u003ecloj-rules-engine\u003c/artifactId\u003e\n  \u003cversion\u003e0.1.2-SNAPSHOT\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\njava code:\n\n```java\n...\ncloj_rules_engine.ClojRules clrules = new cloj_rules_engine.ClojRules();\n...\nclrules.initialize(\"rules.clj\");\n...\n```\n\n- Clojure:\n\n*project.clj*:\n\n```clojure\n[clojars.org/cloj-rules-engine \"0.1.2-SNAPSHOT\"]\n```\n\nclojure code:\n\n```clojure\n(ns ...\n  (:require [cloj-rules-engine.rules-mng :as cre]\n            ...))\n...\n(cre/initialize \"RULES_PATH\")\n...\n```\n\n-----------------------\n\n**Table of Contents**\n\n- [Description](#description)\n  - Features\n    - Main methods\n  - Things to do / limitations / tips\n- [Prerequisites](#prerequisites)\n- [Usage](#usage)\n- [Complex Rules](#complex-rules)\n- [License](#license)\n\n-----------------------\n\n## Description\n\n**cloj-rules-engine** is a rules engine written in Clojure.\n\n![Rules Engine](doc/rules-engine.png)\n\n\n### **Features**:\n\n- Each **rule** has a **condition** (composed of conditional expressions -written in Clojure- that refer to facts), and a set of **actions** that are activated if the condition is satisfied. **Facts** are the data upon which rules operate. The fired actions are a a set of string identifiers.\n\n- Rules are expressed in a simple and easy to read Clojure format ([clojure *maps*](http://www.deadcoderising.com/2015-04-clojure-basics-dealing-wit-maps/))\n\n```clojure\n{\n  :RULE_1 {:cond \"(and (\u003c #A 10) (\u003e #B 50))\"\n           :actions [\"action-1\"]}\n  ...\n}\n```\n\n- Rules with 'probabilities': if condition is satisfied, then the actions have a **probability** of being fired. These probabilities are values between **0** (no chance to be fired - 0%) and **1** (will be fired - 100%). These actions are evaluated in order. This means that if an action is fired, the rest of the actions are ignored.\n\n```clojure\n:RULE_2 {:cond \"(\u003e #A 10)\"\n         :actions [{\"action-B\" 0.5} {\"action-C\" 0.5}]\n```\n\n- Facts can be expressed via clojure maps (Clojure) or via PersistentArrayMap objects (Java):\n\n```clojure\n(update-map-facts {\"#A\" \"14\"})\n```\n\n```java\nPersistentArrayMap facts_map = new PersistentArrayMap(new Object[] {\n\t\t\"#A\", \"14\"\n\t});\nclrules.updateMapFacts(facts_map);\n```\n\n- This library can be used from Java or Clojure code\n\n- Third party libraries used in this project:\n\n| Libs                      | Version       | License                                   |\n| ------------------------- |:-------------:| -----------------------------------------:|\n| [clojure](https://clojure.org/) | 1.8.0 | [![License](https://img.shields.io/badge/License-EPL%201.0-red.svg)](https://opensource.org/licenses/EPL-1.0) |\n| [tools.logging](https://github.com/clojure/tools.logging)  | 0.3.1   | [![License](https://img.shields.io/badge/License-EPL%201.0-red.svg)](https://opensource.org/licenses/EPL-1.0) |\n| [log4j](http://logging.apache.org/log4j/1.2/)  | 1.2.17  | [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) |\n| [data.json](https://github.com/clojure/data.json)   | 0.2.6   | [![License](https://img.shields.io/badge/License-EPL%201.0-red.svg)](https://opensource.org/licenses/EPL-1.0) |\n| [proto-repl](https://github.com/jasongilman/proto-repl) | 0.3.1   | [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) |\n| [math.numeric-tower](https://github.com/clojure/math.numeric-tower/) | 0.0.4 | [![License](https://img.shields.io/badge/License-EPL%201.0-red.svg)](https://opensource.org/licenses/EPL-1.0) |\n| [lein-cloverage](https://github.com/cloverage/cloverage) | 1.0.9 | [![License](https://img.shields.io/badge/License-EPL%201.0-red.svg)](https://opensource.org/licenses/EPL-1.0) |\n\n#### Main methods:\n  - **initialize** loads rules map from absolute or relative path. Returns *true* if everything is okay.\n\n  ```clojure\n  (initialize \"rules.clj\")\n  ```\n\n  ```java\n  clrules.initialize(\"rules.clj\");\n  ```\n\n  - **update-map-facts** update / initialize facts\n\n  ```clojure\n  (update-map-facts {\"#A\" \"14\"})\n  ```\n\n  ```java\n  clrules.updateMapFacts(facts_map);\n  ```\n\n  - **get-rules-actions** evaluates rules based on current facts, and return a list (String) of 'fired' actions\n\n  ```clojure\n  (get-rules-actions)\n  ```\n\n  ```java\n  clrules.getRulesActions();\n  ```\n\n  - **get-fired-rules**\n\n  - **initialize-from-json**\n\n  - **get-rules-actions-probs** (**ONLY Rules with 'probabilities'**) valuates rules based on current facts, and return a list (String) of 'fired' actions\n\n### Things to do / limitations / tips\n\n- (**RULES DEFINITION**) The set of rules are defined using Clojure syntax =\u003e Clojure maps. Parameters / facts have the following formatt: #*FACTNAME*\n  - No underscores allowed.\n  - A *hash* (#) before the fact name\n  - Regular expression used to validate fact / parameter names: `#\"\\#[A-Za-z][A-Za-z0-9]*\"`\n\n\n- (**RULES EVALUATION**) The rules and facts are evaluated following the steps of the next example:\n\n  1. In this example we have 2 facts or parameters: *#A* and *#B*\n\n  2. First, we create a new file to define the rules conditions:\n\n  ```clojure\n  :RULE_1 {:cond \"(and (\u003c #A 10) (\u003e #B 50))\"\n           :actions [\"action-1\"]}\n  ```\n\n  3. Then, when using the library, we set or update the facts\n\n  ```clojure\n  (update-map-facts {\"#A\" 33, \"#B\" 66}))\n  ```\n\n  4. After the new facts' values have been updated, the rules conditions are transformed to clojure syntax in the following way:\n\n  ```clojure\n  (when (and (\u003c 33 10) (\u003e 66 50)) :RULE_1)\n  ```\n\n  5. Finally, if condition is satisfied (using clojure *eval* function inside **get-rules-actions** method), the rule is tagged as fired\n\n\n- (**RULES DEFINITION**) Conditions are clojure expressions surrounded by quotes.\n\n- (**RULES DEFINITION**) When creating the set of rules, use a hash for each of the parameters / facts (i.e. *#A* and *#B*):\n\n```clojure\n:RULE_1 {:cond \"(and (\u003c #A 10) (\u003e #B 50))\"\n         :actions [\"action-1\"]}\n```\n\n- (**RULES DEFINITION**) Use `str` function, single quotes or double quotes (and escape character) if you want to eval String variables.\n\n```clojure\n:RULE_5 {:cond \"(= (str #D) (str 50))\"\n         :actions [\"action-E\"]}\n:RULE_6 {:cond \"(= #D \\\"goldenaxe\\\")\"\n         :actions [\"action-F\"]}\n:RULE_7 {:cond \"(= #D 'goldenaxe2')\"\n        :actions [\"action-G\"]}\n```\n\n- (**TESTING FACTS**) When creating / updating facts, escape string values that will be used as string\n\n```clojure\n(update-map-facts {\"#A\" 15, \"#D\" \"\\\"goldenaxe\\\"\"}))\n```\n\n- If a rule is evaluated and 'fired', it won't be fired until facts are updated. In order to get all the 'fired' rules, call the **get-fired-rules** method / function\n\n-----------------------\n\n## Prerequisites\n\n1. Java version 8\n\n2. [Leiningen][] 2.0.0 or above installed.\n\n[leiningen]: https://github.com/technomancy/leiningen\n\n-----------------------\n\n## Usage\n\nFirst, define a set of rules (*\"rules.clj\"*):\n\n```clojure\n{\n  :RULE_1 {:cond \"(and (\u003c #A 10) (\u003e #B 50))\"\n           :actions [\"action-1\"]\n           :desc \"Rule description: 'launch' action-1 if 'a' is lower than 10 and if 'b' is greater than 50\"}\n\n  :RULE_2 {:cond \"(\u003e #A 10)\"\n           :actions [\"action-2\"]}\n}\n```\n\nAnd then, ...\n\n### From Clojure\n\n```clojure\n(initialize \"rules.clj\")\n(update-map-facts {\"#A\" \"14\"})\n(get-rules-actions)\n(get-fired-rules)\n```\n\nOr...\n\n```clojure\n(if (initialize \"rules.clj\")\n  (when (update-map-facts {\"#A\" \"15\", \"#B\" 13, \"#D\" \"\\\"goldenaxe\\\"\"})\n    (get-rules-actions))\n  false)\n```\n\n### From Java\n\n1. [Create a jar or add dependency to maven](#export-to-maven-local-repo)\n\n2. Java code to use the library:\n\n```java\ncloj_rules_engine.ClojRules clrules = new cloj_rules_engine.ClojRules();\n...\nclrules.initialize(\"rules.clj\");\n\nPersistentArrayMap facts_map = new PersistentArrayMap(new Object[] {\n\t\t\"#A\", \"5\",\n\t\t\"#B\", \"51\"\n\t});\n\nclrules.updateMapFacts(facts_map);\n\nclrules.getRulesActions();\n\nclrules.getFiredRules();  // get fired rules in json format\n\n```\n\n-----------------------\n\n## Complex Rules\n\n- You can use functions from [clojure.math.numeric-tower](https://clojure.github.io/math.numeric-tower/) when defining rules: `expt`, `abs`, `gcd`, `lcm`, `floor` ...\n\n```clojure\n:RULE_8 {:cond \"(\u003e (sqrt #C) 10)\"\n         :actions [\"action-H-sqrt\"]\n         :desc \"Rule description: 'launch' action-H-sqrt if square root of #C is greater than 10.\"}\n```\n\n- You can also use clojure functions (from **org.clojure/clojure**) that return boolean values: `every?`, `even?`, `odd?` ...\n\n```clojure\n:RULE_9 {:cond \"(every? even? (list #A #B #C))\"\n         :actions [\"action-I-even?\"]\n         :desc \"Rule description: 'launch' action-I-even? if all elements from list are even.\"}\n```\n\n- Or custom functions: `#(\u003e % 10)`\n\n```clojure\n:RULE_10 {:cond \"(every? #(\u003e % 10) [#A #B #C])\"\n          :actions [\"action-J-func\"]\n          :desc \"Rule description: 'launch' action-J-func if all elements from list / vector are greater than 10\"}\n```\n\n- (**warning**: not ready yet - only works with **string** vectors or lists) Use lists or vectors as parameters:\n\n```clojure\n:RULE_11 {:cond \"(every? #(\u003e % 100) #LIST1)\"\n          :actions [\"action-K-func\"]\n          :desc \"Rule description: 'launch' action-K-func if all elements from list / vector '#LIST1' are greater than 10\"}\n```\n\n```clojure\n(update-map-facts {\"#A\" \"21\", \"#B\" 43, \"#C\" 1000, \"#LIST1\" \"[121 321 123 122 1233]\"})\n```\n\n-----------------------\n\n## License\n\nCopyright © 2017 Roi Sucasas Font\n\nDistributed under the Eclipse Public License, the same as Clojure.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frsucasasf%2Fcloj-rules-engine","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frsucasasf%2Fcloj-rules-engine","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frsucasasf%2Fcloj-rules-engine/lists"}