-
Notifications
You must be signed in to change notification settings - Fork 31
/
shopping.cljs
130 lines (99 loc) · 3.78 KB
/
shopping.cljs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
(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 defsession]]
[dommy.core :refer [sel sel1]])
(:require [clara.rules.engine :as eng]
[dommy.utils :as utils] ; dommy used to manipulate the DOM.
[dommy.core :as dommy]
[hipo.core :as hipo]
[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) (hipo/create [:.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) (hipo/create [:.promotion (str "Free " type " for promotion " reason)])) )
session)
(defsession example-session 'clara.examples.shopping)
(defn run-examples
"Function to run the above example."
[]
(dommy/append! (sel1 :#shopping) (hipo/create [:.overview "VIP Shopping Example:"]))
;; prints "10 % :vip discount"
(-> example-session
(insert (->Customer :vip)
(->Order 2013 :march 20)
(->Purchase 20 :gizmo)
(->Purchase 120 :widget)) ; Insert some facts.
(fire-rules)
(show-discounts!))
(dommy/append! (sel1 :#shopping) (hipo/create [:.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"
(-> example-session
(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)