1
1
require "date"
2
+ require "semantic"
2
3
3
4
module LaunchDarkly
4
5
module Evaluation
5
6
BUILTINS = [ :key , :ip , :country , :email , :firstName , :lastName , :avatar , :name , :anonymous ]
6
7
8
+ NUMERIC_VERSION_COMPONENTS_REGEX = Regexp . new ( "^[0-9.]*" )
9
+
10
+ DATE_OPERAND = lambda do |v |
11
+ if v . is_a? String
12
+ begin
13
+ DateTime . rfc3339 ( v ) . strftime ( "%Q" ) . to_i
14
+ rescue => e
15
+ nil
16
+ end
17
+ elsif v . is_a? Numeric
18
+ v
19
+ else
20
+ nil
21
+ end
22
+ end
23
+
24
+ SEMVER_OPERAND = lambda do |v |
25
+ if v . is_a? String
26
+ for _ in 0 ..2 do
27
+ begin
28
+ return Semantic ::Version . new ( v )
29
+ rescue ArgumentError
30
+ v = addZeroVersionComponent ( v )
31
+ end
32
+ end
33
+ end
34
+ nil
35
+ end
36
+
37
+ def self . addZeroVersionComponent ( v )
38
+ NUMERIC_VERSION_COMPONENTS_REGEX . match ( v ) { |m |
39
+ m [ 0 ] + ".0" + v [ m [ 0 ] . length ..-1 ]
40
+ }
41
+ end
42
+
43
+ def self . comparator ( converter )
44
+ lambda do |a , b |
45
+ av = converter . call ( a )
46
+ bv = converter . call ( b )
47
+ if !av . nil? && !bv . nil?
48
+ yield av <=> bv
49
+ else
50
+ return false
51
+ end
52
+ end
53
+ end
54
+
7
55
OPERATORS = {
8
56
in :
9
57
lambda do |a , b |
@@ -42,33 +90,15 @@ module Evaluation
42
90
( a . is_a? Numeric ) && ( a >= b )
43
91
end ,
44
92
before :
45
- lambda do |a , b |
46
- begin
47
- if a . is_a? String
48
- a = DateTime . rfc3339 ( a ) . strftime ( '%Q' ) . to_i
49
- end
50
- if b . is_a? String
51
- b = DateTime . rfc3339 ( b ) . strftime ( '%Q' ) . to_i
52
- end
53
- ( a . is_a? Numeric ) ? a < b : false
54
- rescue => e
55
- false
56
- end
57
- end ,
93
+ comparator ( DATE_OPERAND ) { |n | n < 0 } ,
58
94
after :
59
- lambda do |a , b |
60
- begin
61
- if a . is_a? String
62
- a = DateTime . rfc3339 ( a ) . strftime ( "%Q" ) . to_i
63
- end
64
- if b . is_a? String
65
- b = DateTime . rfc3339 ( b ) . strftime ( "%Q" ) . to_i
66
- end
67
- ( a . is_a? Numeric ) ? a > b : false
68
- rescue => e
69
- false
70
- end
71
- end
95
+ comparator ( DATE_OPERAND ) { |n | n > 0 } ,
96
+ semVerEqual :
97
+ comparator ( SEMVER_OPERAND ) { |n | n == 0 } ,
98
+ semVerLessThan :
99
+ comparator ( SEMVER_OPERAND ) { |n | n < 0 } ,
100
+ semVerGreaterThan :
101
+ comparator ( SEMVER_OPERAND ) { |n | n > 0 }
72
102
}
73
103
74
104
class EvaluationError < StandardError
@@ -223,7 +253,10 @@ def variation_for_user(rule, user, flag)
223
253
def bucket_user ( user , key , bucket_by , salt )
224
254
return nil unless user [ :key ]
225
255
226
- id_hash = user_value ( user , bucket_by )
256
+ id_hash = bucketable_string_value ( user_value ( user , bucket_by ) )
257
+ if id_hash . nil?
258
+ return 0.0
259
+ end
227
260
228
261
if user [ :secondary ]
229
262
id_hash += "." + user [ :secondary ]
@@ -235,6 +268,12 @@ def bucket_user(user, key, bucket_by, salt)
235
268
hash_val . to_i ( 16 ) / Float ( 0xFFFFFFFFFFFFFFF )
236
269
end
237
270
271
+ def bucketable_string_value ( value )
272
+ return value if value . is_a? String
273
+ return value . to_s if value . is_a? Integer
274
+ nil
275
+ end
276
+
238
277
def user_value ( user , attribute )
239
278
attribute = attribute . to_sym
240
279
0 commit comments