Skip to content

Commit

Permalink
Clarify the relationship between where, where-event, and match.
Browse files Browse the repository at this point in the history
(where expr) rewrites expr in complex ways, as well as binding
symbols like 'event, 'metric, and 'state. In cases where you want to
write a predicate without symbol capture, (where-event event-name expr)
allowed you to bind the event to an arbitrary symbol inside expr.

We already have a system for binding variables in scope: function
arguments. We also have a large library of functions which take events
and return truthy values. It made sense to consolidate these two into a
new stream: (where* f), which takes a *function* of an event instead of
an expression. (where*) does not rewrite its arguments, except to
provide (where)'s (else ...) blocks.

(where-event) is gone; (where*) provides all its functionality in a more
composable way.

(match) has been underappreciated for a while now. I've made it more
powerful by using the new Match protocol in riemann.common, which allows
it to apply functions, regexes, sets, and values as predicates for
matching aspects of an event. It should be a solid complement to
(where) with these changes.
  • Loading branch information
aphyr committed Nov 11, 2012
1 parent 4d1f932 commit 9a348bd
Show file tree
Hide file tree
Showing 2 changed files with 84 additions and 43 deletions.
53 changes: 33 additions & 20 deletions src/riemann/streams.clj
Expand Up @@ -671,16 +671,23 @@
(riemann.client/send-event client event)))

(defn match
"Passes events on to children only when (f event) is equal to value. If f is
a regex, uses re-matches?"
"Passes events on to children only when (f event) matches value, using
riemann.common/match. For instance:
(match :service nil prn)
(match :state #{\"warning\" \"critical\"} prn)
(match :description #\"error\" prn)
(match :metric 5 prn)
(match expired? true prn)
(match (fn [e] (/ (:metric e) 1000)) 5 prn)
For cases where you only care about whether (f event) is truthy, use (where
some-fn) instead of (match some-fn true)."
[f value & children]
(fn [event]
(let [x (f event)]
(when (if (= (class value) java.util.regex.Pattern)
(re-matches? value x)
(= value x))
(call-rescue event children)
true))))
(when (riemann.common/match value (f event))
(call-rescue event children)
true)))

; Shortcuts for match
;(defn description [value & children] (apply match :description value children))
Expand Down Expand Up @@ -985,18 +992,24 @@
(= 'else (first expr))))
exprs)))

(defmacro where-event
"Passes on events where expr is true, binding the provided symbol
to the event during evaluation.
; Match a event which has expired.
(where-event event (expired? event) ...)"
[event-sym expr & children]
(let [p (where-rewrite expr)]
`(let [kids# [~@children]]
(fn [event#]
(when (let [~event-sym event#] ~p)
(call-rescue event# kids#))))))
(defmacro where*
"A simpler, less magical variant of (where). Instead of binding symbols in
the context of an expression, where* takes a function which takes an event.
When (f event) is truthy, passes event to children--and otherwise, passes
event to (else ...) children. For example:
(where* (fn [e] (< 2 (:metric e))) prn)
(where* expired?
(partial prn \"Expired\")
(else
(partial prn \"Not expired!\")))"
[f & children]
(let [[true-kids else-kids] (where-partition-clauses children)]
`(fn [event#]
(if (~f event#)
(call-rescue event# ~true-kids)
(call-rescue event# ~else-kids)))))

(defmacro where
"Passes on events where expr is true. Expr is rewritten using where-rewrite.
Expand Down
74 changes: 51 additions & 23 deletions test/riemann/test/streams.clj
Expand Up @@ -107,22 +107,31 @@
{:metric 1}]))))

(deftest match-test
(let [r (ref nil)
s (match :service "foo" (fn [e] (dosync (ref-set r e))))]
(s {:service "bar"})
(is (= nil (deref r)))
; Regular strings.
(test-stream (match :service "foo")
[{}
{:service "bar"}
{:service "foo"}]
[{:service "foo"}])

(s {:service "foo"})
(is (= {:service "foo"} (deref r))))

; Regex
(let [r (ref nil)
s (match :service #"^f" (fn [e] (dosync (ref-set r e))))]
(s {:service "bar"})
(is (= nil (deref r)))
; Sets
(test-stream (match :metric #{0 2})
[{}
{:metric 1}
{:metric 2}]
[{:metric 2}])

(s {:service "foo"})
(is (= {:service "foo"} (deref r)))))
; Regexen
(test-stream (match :state #"^mi")
[{}
{:state "migas"}
{:state "other breakfast foods"}]
[{:state "migas"}])

; Functions
(test-stream (match identity 2)
[1 2 3]
[2]))

(deftest tagged-all-test
(test-stream (tagged-all ["kitten" "cat"])
Expand Down Expand Up @@ -163,21 +172,40 @@
[{:tags ["meow" "bark"]}
{:tags ["meow"]}]))

(deftest where-event-test
(let [r (ref [])
s (where-event event
(or (= "good" (:service event))
(< 2 (:metric event)))
(fn [e] (dosync (alter r conj e))))
(deftest where*-test
(test-stream (where* identity)
[true false nil 2]
[true 2])

(test-stream (where* expired?)
[{:time -1 :ttl 0.5}
{:time 0 :ttl 1}]
[{:time -1 :ttl 0.5}])

; Complex closure with else clause
(let [good (atom [])
bad (atom [])
s (where* (fn [event]
(or (= "good" (:service event))
(< 2 (:metric event))))
(partial swap! good conj)
(else (partial swap! bad conj)))
events [{:service "good" :metric 0}
{:service "bad" :metric 0}
{:metric 1}
{:service "bad" :metric 1}
{:service "bad" :metric 3}]
expect [{:service "good" :metric 0}
{:service "bad" :metric 3}]]

; Run stream
(doseq [e events] (s e))
(is (= expect (deref r)))))

(is (= @good
[{:service "good" :metric 0}
{:service "bad" :metric 3}]))
(is (= @bad
[{:service "bad" :metric 0}
{:metric 1}
{:service "bad" :metric 1}]))))

(deftest where-field
(let [r (ref [])
Expand Down

0 comments on commit 9a348bd

Please sign in to comment.