rox-rollout 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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