rox-rollout 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (100) hide show
  1. checksums.yaml +7 -0
  2. data/.circleci/config.yml +45 -0
  3. data/.gitignore +8 -0
  4. data/Gemfile +6 -0
  5. data/LICENSE +50 -0
  6. data/README_DEVELOP.md +19 -0
  7. data/Rakefile +16 -0
  8. data/_archive/.document +5 -0
  9. data/_archive/.rspec +1 -0
  10. data/_archive/Gemfile +15 -0
  11. data/_archive/Gemfile.lock +87 -0
  12. data/_archive/README.md +32 -0
  13. data/_archive/README.rdoc +19 -0
  14. data/_archive/Rakefile +50 -0
  15. data/_archive/lib/expr_function_definition.rb +52 -0
  16. data/_archive/lib/function_definition.rb +48 -0
  17. data/_archive/lib/function_token.rb +12 -0
  18. data/_archive/lib/object_extends.rb +12 -0
  19. data/_archive/lib/ruby_interpreter.rb +292 -0
  20. data/_archive/lib/stack.rb +48 -0
  21. data/_archive/lib/string_extends.rb +14 -0
  22. data/_archive/spec/ruby_interpreter_spec.rb +203 -0
  23. data/_archive/spec/spec_helper.rb +30 -0
  24. data/_archive/spec/stack_spec.rb +77 -0
  25. data/bin/console +14 -0
  26. data/bin/setup +8 -0
  27. data/e2e/container.rb +38 -0
  28. data/e2e/custom_props.rb +55 -0
  29. data/e2e/rox_e2e_test.rb +159 -0
  30. data/e2e/test_vars.rb +24 -0
  31. data/lib/rox.rb +5 -0
  32. data/lib/rox/core/client/buid.rb +82 -0
  33. data/lib/rox/core/client/device_properties.rb +45 -0
  34. data/lib/rox/core/client/internal_flags.rb +20 -0
  35. data/lib/rox/core/client/sdk_settings.rb +5 -0
  36. data/lib/rox/core/configuration/configuration.rb +5 -0
  37. data/lib/rox/core/configuration/configuration_fetched_args.rb +23 -0
  38. data/lib/rox/core/configuration/configuration_fetched_invoker.rb +37 -0
  39. data/lib/rox/core/configuration/configuration_parser.rb +85 -0
  40. data/lib/rox/core/configuration/fetcher_error.rb +13 -0
  41. data/lib/rox/core/configuration/fetcher_status.rb +10 -0
  42. data/lib/rox/core/configuration/models/experiment_model.rb +5 -0
  43. data/lib/rox/core/configuration/models/target_group_model.rb +5 -0
  44. data/lib/rox/core/consts/build.rb +8 -0
  45. data/lib/rox/core/consts/environment.rb +42 -0
  46. data/lib/rox/core/consts/property_type.rb +29 -0
  47. data/lib/rox/core/context/merged_context.rb +16 -0
  48. data/lib/rox/core/core.rb +131 -0
  49. data/lib/rox/core/entities/flag.rb +26 -0
  50. data/lib/rox/core/entities/flag_setter.rb +39 -0
  51. data/lib/rox/core/entities/variant.rb +56 -0
  52. data/lib/rox/core/impression/impression_args.rb +5 -0
  53. data/lib/rox/core/impression/impression_invoker.rb +41 -0
  54. data/lib/rox/core/impression/models/experiment.rb +14 -0
  55. data/lib/rox/core/impression/models/reporting_value.rb +5 -0
  56. data/lib/rox/core/logging/logging.rb +17 -0
  57. data/lib/rox/core/logging/no_op_logger.rb +11 -0
  58. data/lib/rox/core/network/configuration_fetch_result.rb +5 -0
  59. data/lib/rox/core/network/configuration_fetcher.rb +38 -0
  60. data/lib/rox/core/network/configuration_fetcher_base.rb +25 -0
  61. data/lib/rox/core/network/configuration_fetcher_roxy.rb +29 -0
  62. data/lib/rox/core/network/configuration_source.rb +9 -0
  63. data/lib/rox/core/network/request.rb +46 -0
  64. data/lib/rox/core/network/request_configuration_builder.rb +48 -0
  65. data/lib/rox/core/network/request_data.rb +5 -0
  66. data/lib/rox/core/network/response.rb +16 -0
  67. data/lib/rox/core/properties/custom_property.rb +18 -0
  68. data/lib/rox/core/properties/custom_property_type.rb +18 -0
  69. data/lib/rox/core/properties/device_property.rb +11 -0
  70. data/lib/rox/core/register/registerer.rb +35 -0
  71. data/lib/rox/core/reporting/error_reporter.rb +152 -0
  72. data/lib/rox/core/repositories/custom_property_repository.rb +61 -0
  73. data/lib/rox/core/repositories/experiment_repository.rb +21 -0
  74. data/lib/rox/core/repositories/flag_repository.rb +49 -0
  75. data/lib/rox/core/repositories/roxx/experiments_extensions.rb +82 -0
  76. data/lib/rox/core/repositories/roxx/properties_extensions.rb +26 -0
  77. data/lib/rox/core/repositories/target_group_repository.rb +17 -0
  78. data/lib/rox/core/roxx/core_stack.rb +22 -0
  79. data/lib/rox/core/roxx/evaluation_result.rb +28 -0
  80. data/lib/rox/core/roxx/node.rb +11 -0
  81. data/lib/rox/core/roxx/parser.rb +143 -0
  82. data/lib/rox/core/roxx/regular_expression_extensions.rb +33 -0
  83. data/lib/rox/core/roxx/string_tokenizer.rb +68 -0
  84. data/lib/rox/core/roxx/symbols.rb +14 -0
  85. data/lib/rox/core/roxx/token_type.rb +30 -0
  86. data/lib/rox/core/roxx/tokenized_expression.rb +119 -0
  87. data/lib/rox/core/roxx/value_compare_extensions.rb +137 -0
  88. data/lib/rox/core/security/signature_verifier.rb +18 -0
  89. data/lib/rox/core/utils/periodic_task.rb +12 -0
  90. data/lib/rox/core/utils/type_utils.rb +9 -0
  91. data/lib/rox/server/client/sdk_settings.rb +5 -0
  92. data/lib/rox/server/client/server_properties.rb +20 -0
  93. data/lib/rox/server/flags/rox_flag.rb +19 -0
  94. data/lib/rox/server/flags/rox_variant.rb +8 -0
  95. data/lib/rox/server/logging/server_logger.rb +35 -0
  96. data/lib/rox/server/rox_options.rb +27 -0
  97. data/lib/rox/server/rox_server.rb +83 -0
  98. data/lib/rox/version.rb +3 -0
  99. data/rox.gemspec +29 -0
  100. metadata +184 -0
@@ -0,0 +1,12 @@
1
+
2
+ class FunctionToken
3
+ attr :name
4
+ attr :stack_index #to paste value of this function
5
+
6
+ def initialize(name, stack_index)
7
+ @name = name
8
+ @stack_index = stack_index
9
+ end
10
+
11
+ end
12
+
@@ -0,0 +1,12 @@
1
+
2
+ class Object
3
+ FALSE_VALUES = [false, 0, '0', 'f', 'F', 'false', 'FALSE', 'off', 'OFF', 'no', 'NO', 'undefined'].to_set
4
+
5
+ def to_boolean
6
+ if self.to_s == ''
7
+ nil
8
+ else
9
+ !FALSE_VALUES.include?(self.to_s)
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,292 @@
1
+ require 'pry'
2
+ require_relative 'object_extends'
3
+ require_relative 'string_extends'
4
+ require_relative 'function_token'
5
+ require_relative 'function_definition'
6
+ require_relative 'expr_function_definition'
7
+
8
+ class Expression
9
+ attr :expr, :stack
10
+
11
+ def initialize(expr_val, stack)
12
+ @expr = expr_val
13
+ @stack = stack
14
+ end
15
+
16
+ def evaluate
17
+ end
18
+
19
+ def to_s
20
+ @expr
21
+ end
22
+ end
23
+
24
+ class ValueExpression < Expression
25
+ def evaluate
26
+ @expr
27
+ end
28
+ end
29
+
30
+ class UndefinedValue < ValueExpression
31
+ end
32
+
33
+ class StringValue < ValueExpression
34
+ end
35
+
36
+ class BooleanValue < ValueExpression
37
+ def evaluate
38
+ @expr.to_boolean
39
+ end
40
+ end
41
+
42
+ class FloatValue < ValueExpression
43
+ def evaluate
44
+ @expr
45
+ end
46
+ end
47
+
48
+ class FunctionExpression < Expression
49
+ extend ExprFunctionDefinition
50
+
51
+ attr :evaluators
52
+
53
+ unary_bool_func :not, '!'
54
+
55
+ bool_func :and, '&'
56
+ bool_func :or, '|'
57
+
58
+ comp_func :eq, '=='
59
+ comp_func :ne, '!='
60
+ comp_func :lt, '<'
61
+ comp_func :lte, '<='
62
+ comp_func :gt, '>'
63
+ comp_func :gte, '>='
64
+
65
+ def initialize(expr_val, stack)
66
+ super(expr_val, stack)
67
+
68
+ @functions = ['isUndefined', 'now']
69
+ @functions << self.class.instance_variable_get('@func_list')
70
+ @functions.flatten!
71
+
72
+ prepare_evaluators()
73
+ end
74
+
75
+ def evaluate
76
+ ev = @evaluators[@expr]
77
+ return ev.call unless ev.nil?
78
+
79
+ #todo log invalid function
80
+ @stack.push BooleanValue.new(false, @stack) if ev.nil?
81
+ end
82
+
83
+ private
84
+
85
+ def prepare_evaluators()
86
+ @evaluators = @functions.map{|f| [f, ->{ self.send("evaluate_#{f}")}]}.to_h
87
+ end
88
+
89
+ def evaluate_isUndefined
90
+ a = @stack.pop
91
+ @stack.push BooleanValue.new(a.evaluate == :undefined, @stack)
92
+ end
93
+
94
+ def evaluate_now
95
+ Time.now.to_f.round(3)*1000
96
+ end
97
+
98
+ end
99
+
100
+ ## RubyInterpreter ##
101
+ #
102
+ # goal: Interpret expression in forward polish notation with some functions
103
+ #
104
+ class RubyInterpreter
105
+ extend FunctionDefinition
106
+
107
+ attr_reader :func_stack, :operands_stack
108
+ attr_reader :expr_stack
109
+
110
+ #operators
111
+ unary_bool_func :not, '!'
112
+
113
+ bool_func :and, '&'
114
+ bool_func :or, '|'
115
+
116
+ comp_func :eq, '=='
117
+ comp_func :ne, '!='
118
+
119
+ comp_func :lt, '<'
120
+ comp_func :lte, '<='
121
+ comp_func :gt, '>'
122
+ comp_func :gte, '>='
123
+
124
+ def initialize
125
+ @func_stack = Stack.new
126
+ @operands_stack = Stack.new
127
+ @expr_stack = Stack.new
128
+
129
+ @func_list ||= []
130
+ @func_list << self.class.instance_variable_get('@func_list')
131
+ @func_list << ['isUndefined', 'now']
132
+ @func_list.flatten!
133
+
134
+ @funcs_re ||= Regexp.new(@func_list.join('|') )
135
+ end
136
+ #todo make unary/2ry functions by define_method (dry code)
137
+
138
+ def prepare_expr_stack(tokens)
139
+ is_string_token = false
140
+ string_token = ''
141
+
142
+ tokens.each do |tkn, i|
143
+
144
+ if is_string_token && tkn != '"'
145
+ string_token += tkn
146
+ next
147
+ end
148
+
149
+ case tkn
150
+ when 'undefined'
151
+ @expr_stack.push( UndefinedValue.new(tkn.to_sym, @expr_stack) )
152
+ when '"'
153
+ if( is_string_token ) #todo check pair "
154
+ is_string_token = false
155
+ @expr_stack.push StringValue.new(string_token, @expr_stack)
156
+ string_token = ''
157
+ else
158
+ is_string_token = true
159
+ end
160
+ when ',' , '(', ')', ' '
161
+ #do nothing
162
+ when @funcs_re
163
+ @expr_stack.push( FunctionExpression.new( tkn , @expr_stack) )
164
+ when 'true', 'false'
165
+ @expr_stack.push BooleanValue.new(tkn, @expr_stack)
166
+ else
167
+ fval, is_float = tkn.to_float
168
+ if(is_float)
169
+ @expr_stack.push FloatValue.new(fval, @expr_stack)
170
+ else
171
+ #todo show error message 'unknown token'
172
+ return 'false'
173
+ end
174
+ end
175
+ end
176
+ end
177
+
178
+ def evaluate_expr_stack()
179
+ @func_evaluators = @func_list.map{|f| [f, ->(){ self.send("evaluate_func_#{f}")}]}.to_h
180
+
181
+ value = false
182
+
183
+ while !@expr_stack.empty? do
184
+ expr = @expr_stack.pop
185
+ value = expr.evaluate unless expr.nil?
186
+
187
+ return false if expr.nil? #todo show error message
188
+ end
189
+
190
+ value
191
+ end
192
+
193
+
194
+ def evaluateExpression2(expr)
195
+ @expr_stack.clear
196
+
197
+ tokens = expr.scan(/\d+\.?\d*|\s+|,|\(|"|\)|\w+|\W+/)
198
+
199
+ prepare_expr_stack(tokens)
200
+ expr_stack.invert()
201
+ evaluate_expr_stack()
202
+ end
203
+
204
+ # Evaluate expression
205
+ #
206
+ # info:
207
+ # 1. Language is polish forward notation with custom functions
208
+ # 2. Function list:
209
+ # a. and(a,b)
210
+ #
211
+ def evaluateExpression(expr)
212
+ @func_stack.clear
213
+ @operands_stack.clear
214
+
215
+
216
+ tokens = expr.scan(/\d+\.?\d*|\s+|,|\(|"|\)|\w+|\W+/)
217
+
218
+ is_string_token = false
219
+ string_token = ''
220
+
221
+ @funcs_re ||= Regexp.new(@func_list.join('|') )
222
+
223
+ #prepare stack
224
+ tokens.each do |tkn, i|
225
+ if is_string_token && tkn != '"'
226
+ string_token += tkn
227
+ next
228
+ end
229
+
230
+ case tkn
231
+ when 'undefined'
232
+ @operands_stack.push( tkn.to_sym )
233
+ when '"'
234
+ if( is_string_token ) #todo check pair "
235
+ is_string_token = false
236
+ @operands_stack.push string_token
237
+ string_token = ''
238
+ else
239
+ is_string_token = true
240
+ end
241
+ when ',' , '(', ')', ' '
242
+ #do nothing
243
+ when @funcs_re
244
+ @func_stack.push( FunctionToken.new( tkn, @operands_stack.size ) )
245
+ when 'true', 'false'
246
+ @operands_stack.push tkn
247
+ else
248
+ fval, is_float = tkn.to_float
249
+ if(is_float)
250
+ @operands_stack.push(fval)
251
+ else
252
+ #todo show error message 'unknown token'
253
+ return 'false'
254
+ end
255
+ end
256
+ end
257
+
258
+ #evaluate stack
259
+ @func_evals ||= prepare_func_evaluators()
260
+
261
+ while !@func_stack.empty? do
262
+
263
+ token = @func_stack.pop
264
+
265
+ feval = @func_evals[token.name]
266
+
267
+ feval.call(token.stack_index) unless feval.nil?
268
+ end
269
+
270
+ @operands_stack.pop
271
+ end
272
+
273
+ private
274
+
275
+ def prepare_func_evaluators()
276
+ # make hash with functors: 'or' => ->(stack_index){ self.send('evaluateOr', stack_index) }
277
+ return @func_list.map{|f| [f, ->(stack_index){ self.send("evaluate#{f[0].upcase}#{f[1..-1]}", stack_index) }]}.to_h
278
+ end
279
+
280
+ #evaluators
281
+
282
+ def evaluateIsUndefined(stack_index)
283
+ a = @operands_stack.pop
284
+ @operands_stack.push (a == :undefined).to_s
285
+ end
286
+
287
+ def evaluateNow(stack_index)
288
+ @operands_stack.insert Time.now().tv_sec.to_f, stack_index
289
+ end
290
+
291
+
292
+ end
@@ -0,0 +1,48 @@
1
+ class Stack
2
+
3
+ class EmptyError < StandardError
4
+ end
5
+
6
+ def initialize
7
+ @elements = []
8
+ end
9
+
10
+ def push(value)
11
+ @elements.push value
12
+ value
13
+ end
14
+
15
+ def pop
16
+ raise EmptyError.new('Stack is empty') if empty?
17
+
18
+ @elements.pop
19
+ end
20
+
21
+ def poke(value, index)
22
+ @elements[index] = value
23
+ end
24
+
25
+ def insert(value, index)
26
+ @elements.insert(index, value)
27
+ end
28
+
29
+ def clear
30
+ @elements.clear
31
+ end
32
+
33
+ def empty?
34
+ @elements.size == 0
35
+ end
36
+
37
+ def to_s
38
+ @elements.join(',')
39
+ end
40
+
41
+ def size
42
+ @elements.size
43
+ end
44
+
45
+ def invert
46
+ @elements.reverse!
47
+ end
48
+ end
@@ -0,0 +1,14 @@
1
+
2
+ class String
3
+ # convert string to float
4
+ #
5
+ # retval:
6
+ # val - float value (or nan? if not float)
7
+ # is_float - bool if val is float
8
+ #
9
+ def to_float
10
+ fval = 0.0
11
+ fval = Float(self) rescue 0.0/0.0
12
+ return fval, !fval.nan?
13
+ end
14
+ end
@@ -0,0 +1,203 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe 'RubyInterpreter' do
4
+ let(:parser){ RubyInterpreter.new }
5
+
6
+ def check_parser_clean
7
+ expect(parser.operands_stack.empty?).to eq(true)
8
+ expect(parser.func_stack.empty?).to eq(true)
9
+ end
10
+
11
+ def check_parsing(expr, result)
12
+ expect(parser.evaluateExpression(expr)).to eq(result)
13
+ check_parser_clean
14
+ end
15
+
16
+ def check_parser_clean2
17
+ expect(parser.expr_stack.empty?).to eq(true)
18
+ end
19
+
20
+ def check_parsing2(expr, result)
21
+ expect(parser.evaluateExpression2(expr)).to eq(result)
22
+ check_parser_clean2
23
+ end
24
+
25
+ context 'and function' do
26
+ it 'checks and(true,true) == true' do
27
+ check_parsing2('and(true, true)', true)
28
+ end
29
+
30
+ it 'checks and(true,false) == false' do
31
+ check_parsing2('and(true, false)', false)
32
+ end
33
+
34
+ it 'checks and(false,true) == false' do
35
+ check_parsing2('and(false, true)', false)
36
+ end
37
+
38
+ it 'checks and(false,false) == false' do
39
+ check_parsing2('and(false, false)', false)
40
+ end
41
+ end
42
+
43
+ context 'or function' do
44
+ it 'checks or(true, true) == true' do
45
+ check_parsing2('or(true, true)', true)
46
+ end
47
+
48
+ it 'checks or(true, false) == true' do
49
+ check_parsing2('or(true, false)', true)
50
+ end
51
+
52
+ it 'checks or(false, true) == true' do
53
+ check_parsing2('or(false, true)', true)
54
+ end
55
+
56
+ it 'checks or(false, false) == false' do
57
+ check_parsing2('or(false, false)', false)
58
+ end
59
+ end
60
+
61
+ context 'not function' do
62
+ it 'checks not(false) == true' do
63
+ check_parsing2('not(false)', true)
64
+ end
65
+
66
+ it 'checks not(true) == false' do
67
+ check_parsing2('not(true)', false)
68
+ end
69
+
70
+ it 'checks not(undefined) == false' do
71
+ check_parsing2('not(undefined)', true)
72
+ end
73
+ end
74
+
75
+ context 'eq function' do
76
+ it 'checks eq("la la", "la la")) == true' do
77
+ check_parsing2('eq("la la", "la la")', true);
78
+ end
79
+
80
+ it 'checks eq("la la", "la,la")) == false' do
81
+ check_parsing2('eq("la la", "la,la")', false);
82
+ end
83
+
84
+ it 'checks eq("lala", "lala") == true' do
85
+ check_parsing2('eq("lala", "lala")', true)
86
+ end
87
+
88
+ it 'checks eq(undefined, undefined) == true' do
89
+ check_parsing2('eq(undefined, undefined)', true)
90
+ end
91
+ end
92
+
93
+ context 'lt function' do
94
+ it 'checks lt(500,100) == false' do
95
+ check_parsing2('lt(500, 100)', false)
96
+ end
97
+
98
+ it 'checks lt(500,500) == false' do
99
+ check_parsing2('lt(500, 500)', false)
100
+ end
101
+
102
+ it 'checks lt(500,500.54) == true' do
103
+ check_parsing2('lt(500, 500.54)', true)
104
+ end
105
+ end
106
+
107
+ context 'lte function' do
108
+ it 'checks lte(500, 500) == true' do
109
+ check_parsing2('lte(500, 500)', true)
110
+ end
111
+ end
112
+
113
+ context 'gt function' do
114
+ it 'checks gt(500, 100) == true' do
115
+ check_parsing2('gt(500,100)',true)
116
+ end
117
+
118
+ it 'checks gt(500, 500) == false' do
119
+ check_parsing2('gt(500,500)',false)
120
+ end
121
+
122
+ it 'checks gt(500.54, 500) == true' do
123
+ check_parsing2('gt(500.54,500)',true)
124
+ end
125
+ end
126
+
127
+ context 'gte function' do
128
+ it 'checks gte(500, 500) == true' do
129
+ check_parsing2('gte(500,500)',true)
130
+ end
131
+
132
+ it 'checks gte("500", 500) == false' do
133
+ check_parsing2('gte("500",500)',false)
134
+ end
135
+ end
136
+
137
+ context 'isUndefined function' do
138
+ it 'checks isUndefined(undefined) == true' do
139
+ check_parsing2('isUndefined(undefined)', true)
140
+ end
141
+
142
+ it 'checks isUndefined("undefined") == false' do
143
+ check_parsing2('isUndefined("undefined")', false)
144
+ end
145
+
146
+ it 'checks isUndefined(123123) == false' do
147
+ check_parsing2('isUndefined(123123)', false)
148
+ end
149
+ end
150
+
151
+ context 'now() function' do
152
+ it 'checks gte(now(), now()) == true' do
153
+ check_parsing2('gte(now(), now())', true)
154
+ end
155
+
156
+ it 'checks gte(now(), 2458.123) == true' do
157
+ check_parsing2('gte(now(), 2458.123)', true)
158
+ end
159
+ end
160
+
161
+ context 'complex checks' do
162
+ it 'checks and(true, or(true, true)) == true' do
163
+ check_parsing2('and(true, or(true, true))', true);
164
+ end
165
+
166
+ it 'checks and(true, or(false, true)) == true' do
167
+ check_parsing2('and(true, or(false, true))', true);
168
+ end
169
+
170
+ it 'checks and(true, or(false, false)) == false' do
171
+ check_parsing2('and(true, or(false, false))', false);
172
+ end
173
+
174
+ it 'checks not(and(false, or(false, true))) == true' do
175
+ check_parsing2('not(and(false, or(false, true)))', true);
176
+ end
177
+
178
+ it 'checks ne(100.123, 100.321) == true' do
179
+ check_parsing2('ne(100.123, 100.321)', true)
180
+ end
181
+
182
+ it 'checks not(eq(undefined, undefined)) == false' do
183
+ check_parsing('not(eq(undefined, undefined))', 'false')
184
+ end
185
+
186
+ it 'checks not(eq(not(undefined), not(undefined))) == true' do
187
+ check_parsing2('not(eq(not(undefined), not(undefined)))', true )
188
+ end
189
+
190
+ end
191
+
192
+ context 'error checking' do
193
+ it 'checks NOT_AN_OPERATOR(500, 500) == false' do
194
+ check_parsing('NOT_AN_OPERATOR(500,500)', 'false')
195
+ end
196
+
197
+ it 'checks JUSTAWORD(500, 500) == false' do
198
+ check_parsing('JUSTAWORD(500,500)', 'false')
199
+ end
200
+ end
201
+
202
+ #todo check non-matched pairs
203
+ end