-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathbacon.matchers.coffee
124 lines (118 loc) · 3.98 KB
/
bacon.matchers.coffee
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
init = (Bacon) ->
toFieldExtractor = (f) ->
parts = f.slice(1).split(".")
partFuncs = Bacon._.map(toSimpleExtractor, parts)
(value) ->
for f in partFuncs
value = f(value)
value
toSimpleExtractor = (key) -> (value) ->
if not value?
undefined
else
value[key]
contains = (container, item) ->
if container instanceof Array or typeof container == 'string'
container.indexOf(item) >= 0
else if typeof container == 'object'
matchingKeyValuePairs = 0
Object.keys(item).forEach (bKey) -> # Object.keys works in IE9 and above
containerHasItemKeyAndValue = container.hasOwnProperty(bKey) and container[bKey] == item[bKey]
if containerHasItemKeyAndValue
matchingKeyValuePairs += 1
containerHasAllKeyValuesOfItem = matchingKeyValuePairs == Object.keys(item).length
itemIsNotEmpty = Object.keys(item).length > 0
containerHasAllKeyValuesOfItem and itemIsNotEmpty
else
false
addMatchers = (apply1, apply2, apply3, stream, operation) ->
context = {}
addPositiveMatchers context, apply1, apply2, apply3
addClauseMatchers context, stream, operation
context["not"] = ->
negatedContext = {}
applyNot1 = (f) -> apply1 (a) -> not f(a)
applyNot2 = (f) -> apply2 (a, b) -> not f(a, b)
applyNot3 = (f) -> apply3 (val, a, b) -> not f(val, a, b)
addPositiveMatchers negatedContext, applyNot1, applyNot2, applyNot3
addClauseMatchers negatedContext, stream, (s) -> operation(s.not())
context
addClauseMatchers = (context, stream, operation) ->
context["some"] = (fs...) ->
matches = Bacon._.map(((f) -> f(stream)), fs)
isSome = Bacon._.fold(matches, Bacon.constant(false), (x,y) -> x.or(y))
operation(isSome)
context["every"] = (fs...) ->
matches = Bacon._.map(((f) -> f(stream)), fs)
isEvery = Bacon._.fold(matches, Bacon.constant(true), (x,y) -> x.and(y))
operation(isEvery)
context
addPositiveMatchers = (context, apply1, apply2, apply3) ->
context["lessThan"] = apply2((a, b) ->
a < b
)
context["lessThanOrEqualTo"] = apply2((a, b) ->
a <= b
)
context["greaterThan"] = apply2((a, b) ->
a > b
)
context["greaterThanOrEqualTo"] = apply2((a, b) ->
a >= b
)
context["equalTo"] = apply2((a, b) ->
a is b
)
context["truthy"] = apply1((a) ->
!!a
)
context["match"] = apply2((val, pattern) ->
pattern.test val
)
context["inOpenRange"] = apply3((val, a, b) ->
a < val < b
)
context["inClosedRange"] = apply3((val, a, b) ->
a <= val <= b
)
context["containerOf"] = apply2((a, b) ->
contains(a,b)
)
context["memberOf"] = apply2((a, b) ->
contains(b,a)
)
context
asMatchers = (stream, operation, combinator, fieldKey) ->
field = if fieldKey? then toFieldExtractor(fieldKey) else Bacon._.id
apply1 = (f) ->
->
operation (val) -> f(field(val))
apply2 = (f) ->
(other) ->
if other instanceof Bacon.Observable
combinator other, (val, other) -> f(field(val), other)
else
operation (val) -> f(field(val), other)
apply3 = (f) ->
(first, second) ->
operation (val) -> f(field(val), first, second)
addMatchers apply1, apply2, apply3, stream.map(field), operation
Bacon.Observable::is = (fieldKey) ->
context = this
operation = (f) -> context.map(f)
combinator = (observable, f) -> context.combine(observable, f)
asMatchers(context, operation, combinator, fieldKey)
Bacon.Observable::where = (fieldKey) ->
context = this
operation = (f) -> context.filter(f)
combinator = (observable, f) -> context.filter context.combine(observable, f)
asMatchers(context, operation, combinator, fieldKey)
Bacon
if module?
Bacon = require("baconjs")
module.exports = init(Bacon)
else
if typeof require is "function"
define "bacon.matchers", ["bacon"], init
else
init(this.Bacon)