Skip to content

Commit

Permalink
Add ClojureScript example.
Browse files Browse the repository at this point in the history
  • Loading branch information
Ryan Brush committed Nov 29, 2013
1 parent ea66afd commit 98b63c1
Show file tree
Hide file tree
Showing 5 changed files with 178 additions and 6 deletions.
15 changes: 12 additions & 3 deletions README.md
@@ -1,13 +1,22 @@
# clara-examples

Example rules using the Clara rules engine
Example usage of Clara.

## Usage

See the code for examples using Clara. Launching the project via "lein run" will run all examples.
See the code for examples using Clara.

### In-memory
Simply typing ```lein run``` in the project's root directory will run the [simple, in-memory examples](https://github.com/rbrush/clara-examples/tree/master/src/main/clojure/clara/examples).

### ClojureScript
Compile the ClojureScript code with ```lein compile``` or ```lein cljsbuild once```, and then open the ```resources/public/index.html``` file in a browser will run the [ClojureScript examples](https://github.com/rbrush/clara-examples/tree/master/rc/main/clojurescript/clara/examples) and display the result.

### Storm
Typing ```lein run -m clara.examples.storm``` will start an in-process Storm topology and run the storm example.

## License

Copyright © 2013 FIXME
Copyright © 2013 Ryan Brush

Distributed under the Eclipse Public License, the same as Clojure.
24 changes: 21 additions & 3 deletions project.clj
@@ -1,16 +1,34 @@
(defproject org.toomuchcode/clara-examples "0.1.1"
(defproject org.toomuchcode/clara-examples "0.2.0-SNAPSHOT"
:description "Clara Example Rules"
:url "https://github.com/rbrush/clara-examples"
:license {:name "Eclipse Public License"
:url "http://www.eclipse.org/legal/epl-v10.html"}
:dependencies [[org.clojure/clojure "1.5.1"]
:dependencies [[com.google.guava/guava "15.0"] ; Explicitly pull new Guava version for dependency conflicts.
[org.clojure/clojure "1.5.1"]
[org.toomuchcode/clara-rules "0.3.0-SNAPSHOT"]
[org.toomuchcode/clara-storm "0.1.0-SNAPSHOT"]
[clj-time "0.5.1"]]

;; Dependencies for ClojureScript example.
[prismatic/dommy "0.1.1"]
[org.clojure/clojurescript "0.0-2030"]

;; Dependency for time-based rules example.
[clj-time "0.6.0"]]

:plugins [[lein-cljsbuild "1.0.0"]]
:source-paths ["src/main/clojure"]
:test-paths ["src/test/clojure"]
:java-source-paths ["src/main/java"]
:main clara.examples
:hooks [leiningen.cljsbuild]
:cljsbuild {:builds [{:source-paths ["src/main/clojurescript"]
:jar true
:compiler {:output-to "resources/public/js/examples.js"
:optimizations :advanced}}]}

;; Austin for the ClojureScript REPL.
:profiles {:dev {:plugins [[com.cemerick/austin "0.1.3"]]}}

:scm {:name "git"
:url "https://github.com/rbrush/clara-examples.git"}
:pom-addition [:developers [:developer {:id "rbrush"}
Expand Down
14 changes: 14 additions & 0 deletions resources/public/index.html
@@ -0,0 +1,14 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Clara Examples</title>
</head>

<body>
<h1>Clara Examples</h1>
<h2>Shopping Example</h2>
<p id="shopping"></p>
<script src="js/examples.js"></script>
</body>
</html>
5 changes: 5 additions & 0 deletions src/main/clojurescript/clara/examples.cljs
@@ -0,0 +1,5 @@
(ns clara.examples
(:require [clara.examples.shopping :as shopping]))

;; Run the shopping examples.
(shopping/run-examples)
126 changes: 126 additions & 0 deletions src/main/clojurescript/clara/examples/shopping.cljs
@@ -0,0 +1,126 @@
(ns clara.examples.shopping
"Shopping example adapted to ClojureScript, updating the DOM to display results.
Real examples would actually have styling."
(:require-macros [clara.macros :refer [defrule defquery mk-session]]
[dommy.macros :refer [node sel sel1]])
(:require [dommy.utils :as utils] ; dommy used to manipulate the DOM.
[dommy.core :as dommy]
[clara.rules.accumulators :as acc]
[clara.rules :refer [insert retract fire-rules query insert! retract!]]))


;;;; Facts used in the examples below.

(defrecord Order [year month day])

(defrecord Customer [status])

(defrecord Purchase [cost item])

(defrecord Discount [reason percent])

(defrecord Total [total])

(defrecord Promotion [reason type])

;;;; Some example rules. ;;;;

(defrule total-purchases
(?total <- (acc/sum :cost) :from [Purchase])
=>
(insert! (->Total ?total)))

;;; Discounts.
(defrule summer-special
"Place an order in the summer and get 20% off!"
[Order (#{:june :july :august} month)]
=>
(insert! (->Discount :summer-special 20)))

(defrule vip-discount
"VIPs get a discount on purchases over $100. Cannot be combined with any other discount."
[Customer (= status :vip)]
[Total (> total 100)]
=>
(insert! (->Discount :vip 10)))

(def max-discount
"Accumulator that returns the highest percentage discount."
(acc/max :percent :returns-fact true))

(defquery get-best-discount
"Query to find the best discount that can be applied"
[]
[?discount <- max-discount :from [Discount]])

;;; Promotions.
(defrule free-widget-month
"All purchases over $200 in August get a free widget."
[Order (= :august month)]
[Total (> total 200)]
=>
(insert! (->Promotion :free-widget-month :widget)))

(defrule free-lunch-with-gizmo
"Anyone who purchases a gizmo gets a free lunch."
[Purchase (= item :gizmo)]
=>
(insert! (->Promotion :free-lunch-with-gizmo :lunch)))

(defquery get-promotions
"Query to find promotions for the purchase."
[]
[?promotion <- Promotion])

;;;; The section below shows this example in action. ;;;;

(defn show-discounts!
"Print the discounts from the given session."
[session]

;; Destructure and print each discount.
(doseq [{{reason :reason percent :percent} :?discount} (query session get-best-discount)]
(dommy/append! (sel1 :#shopping) [:.discount (str percent "% " reason " discount")]) )

session)

(defn show-promotions!
"Prints promotions from the given session"
[session]

(doseq [{{reason :reason type :type} :?promotion} (query session get-promotions)]
(dommy/append! (sel1 :#shopping) [:.promotion (str "Free " type " for promotion " reason)]) )

session)

(defn run-examples
"Function to run the above example."
[]

(dommy/append! (sel1 :#shopping) [:.overview "VIP Shopping Example:"])

;; prints "10 % :vip discount"
(-> (mk-session 'clara.examples.shopping) ; Load the rules.
(insert (->Customer :vip)
(->Order 2013 :march 20)
(->Purchase 20 :gizmo)
(->Purchase 120 :widget)) ; Insert some facts.
(fire-rules)
(show-discounts!))

(dommy/append! (sel1 :#shopping) [:.overview "Summer special and widget promotion example:"])

;; prints: "20 % :summer-special discount"
;; "Free :lunch for promotion :free-lunch-for-gizmo"
;; "Free :widget for promotion :free-widget-month"
(-> (mk-session 'clara.examples.shopping) ; Load the rules.
(insert (->Customer :vip)
(->Order 2013 :august 20)
(->Purchase 20 :gizmo)
(->Purchase 120 :widget)
(->Purchase 90 :widget)) ; Insert some facts.
(fire-rules)
(show-discounts!)
(show-promotions!))

nil)

0 comments on commit 98b63c1

Please sign in to comment.