yggdrasil-engine 0.0.2 → 0.0.3.beta.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 21b1c99a3a6a9c6d35c3af13692efcf7591c45e2eb314935f3a54769c5f9718c
4
- data.tar.gz: fe5b410a9889dd7ab6a70604751945fecca7d15252b302c7a96c4d0b31f48c04
3
+ metadata.gz: 374876a9c18a7d7bec48b8910ed683aa6985a29d177259387c49b91784312e1a
4
+ data.tar.gz: 55b60e48ea625e9a4bfe34ecd405cea25bbd6c3b9487b8f80ba7e219dcb9637f
5
5
  SHA512:
6
- metadata.gz: 1ab7165b8b120049c664cfe62c589c29a120f2bc1c5cf2d1fff27a25de8b13a045428fbc70a38d3089be935f233c75f0c2df2c5294808f3c3f08de627d6f64fa
7
- data.tar.gz: 84e728ba1971ae2d1d192a680b5c67311fad83258608dc2c939ddba8ebb6782b9bb8d76cf77e167a9049942442b768f5f9737d4f49c52b8ff5eb8c4d986c5456
6
+ metadata.gz: dcac8c9b7d0d4db83844f6f9f32b832d1c9cee4e8176e84d8c100960d6cefc6dd51bb1dc0f5b83d4d14d808c3ce9881fceffcbcde6bd44aef93bc6665bac0540
7
+ data.tar.gz: 4881b0ae99329cb46ceb1f2dd041e08074b349b54201201f014dedf4843b9e689ab15924f634971aa19aae2297a64f6a6e5e9adb4880e607c3dcf6e5e4cb5cdd
data/README.md CHANGED
@@ -1,36 +1,36 @@
1
- # Ruby Bindings to Yggdrasil
2
-
3
- ## Running the tests
4
-
5
- First make sure that you have built the native FFI code and it's located in the right place. This can be done with the build script in `build.sh`:
6
-
7
-
8
- ```bash
9
- ./build.sh
10
- ```
11
-
12
- Then you can run the tests with:
13
-
14
- ```bash
15
- rspec
16
- ```
17
-
18
- There's also a `mem_check.rb` in the scripts folder. This is not a bullet proof test, but it can be helpful for detecting large leaks. This requires human interaction - you need to read the output and understand what it's telling you, so it's not run as part of the test suite.
19
-
20
- ```bash
21
- ruby scripts/mem_check.rb
22
- ```
23
-
24
- ## Build
25
-
26
- You can build the gem with:
27
-
28
- ```bash
29
- gem build yggdrasil-engine.gemspec
30
- ```
31
-
32
- Then you can install the gem for local development with:
33
-
34
- ```
35
- gem install yggdrasil-engine-0.0.1.gem
36
- ```
1
+ # Ruby Bindings to Yggdrasil
2
+
3
+ ## Running the tests
4
+
5
+ First make sure that you have built the native FFI code and it's located in the right place. This can be done with the build script in `build.sh`:
6
+
7
+
8
+ ```bash
9
+ ./build.sh
10
+ ```
11
+
12
+ Then you can run the tests with:
13
+
14
+ ```bash
15
+ rspec
16
+ ```
17
+
18
+ There's also a `mem_check.rb` in the scripts folder. This is not a bullet proof test, but it can be helpful for detecting large leaks. This requires human interaction - you need to read the output and understand what it's telling you, so it's not run as part of the test suite.
19
+
20
+ ```bash
21
+ ruby scripts/mem_check.rb
22
+ ```
23
+
24
+ ## Build
25
+
26
+ You can build the gem with:
27
+
28
+ ```bash
29
+ gem build yggdrasil-engine.gemspec
30
+ ```
31
+
32
+ Then you can install the gem for local development with:
33
+
34
+ ```
35
+ gem install yggdrasil-engine-0.0.1.gem
36
+ ```
@@ -1,60 +1,60 @@
1
- STANDARD_STRATEGIES = [
2
- "default",
3
- "userWithId",
4
- "gradualRolloutUserId",
5
- "gradualRolloutSessionId",
6
- "gradualRolloutRandom",
7
- "flexibleRollout",
8
- "remoteAddress",
9
- ].freeze
10
-
11
- class CustomStrategyHandler
12
- def initialize
13
- @custom_strategies_definitions = {}
14
- @custom_strategy_implementations = {}
15
- end
16
-
17
- def update_strategies(json_str)
18
- custom_strategies = {}
19
- parsed_json = JSON.parse(json_str)
20
-
21
- parsed_json["features"].each do |feature|
22
- toggle_name = feature["name"]
23
- strategies = feature["strategies"]
24
-
25
- custom_strategies_for_toggle = strategies.select do |strategy|
26
- !STANDARD_STRATEGIES.include?(strategy["name"])
27
- end
28
-
29
- unless custom_strategies_for_toggle.empty?
30
- custom_strategies[toggle_name] = custom_strategies_for_toggle
31
- end
32
- end
33
-
34
- @custom_strategies_definitions = custom_strategies
35
- end
36
-
37
- def register_custom_strategies(strategies)
38
- strategies.each do |strategy|
39
- if strategy.respond_to?(:name) && strategy.name.is_a?(String) &&
40
- strategy.respond_to?(:enabled?)
41
- @custom_strategy_implementations[strategy.name] = strategy
42
- else
43
- raise "Invalid strategy object. Must have a name method that returns a String and an enabled? method."
44
- end
45
- end
46
- end
47
-
48
- def evaluate_custom_strategies(toggle_name, context)
49
- results = {}
50
-
51
- @custom_strategies_definitions[toggle_name]&.each_with_index do |strategy, index|
52
- key = "customStrategy#{index + 1}"
53
- strategy_impl = @custom_strategy_implementations[strategy["name"]]
54
- result = strategy_impl&.enabled?(strategy["parameters"], context) || false
55
- results[key] = result
56
- end
57
-
58
- results
59
- end
60
- end
1
+ STANDARD_STRATEGIES = [
2
+ "default",
3
+ "userWithId",
4
+ "gradualRolloutUserId",
5
+ "gradualRolloutSessionId",
6
+ "gradualRolloutRandom",
7
+ "flexibleRollout",
8
+ "remoteAddress",
9
+ ].freeze
10
+
11
+ class CustomStrategyHandler
12
+ def initialize
13
+ @custom_strategies_definitions = {}
14
+ @custom_strategy_implementations = {}
15
+ end
16
+
17
+ def update_strategies(json_str)
18
+ custom_strategies = {}
19
+ parsed_json = JSON.parse(json_str)
20
+
21
+ parsed_json["features"].each do |feature|
22
+ toggle_name = feature["name"]
23
+ strategies = feature["strategies"]
24
+
25
+ custom_strategies_for_toggle = strategies.select do |strategy|
26
+ !STANDARD_STRATEGIES.include?(strategy["name"])
27
+ end
28
+
29
+ unless custom_strategies_for_toggle.empty?
30
+ custom_strategies[toggle_name] = custom_strategies_for_toggle
31
+ end
32
+ end
33
+
34
+ @custom_strategies_definitions = custom_strategies
35
+ end
36
+
37
+ def register_custom_strategies(strategies)
38
+ strategies.each do |strategy|
39
+ if strategy.respond_to?(:name) && strategy.name.is_a?(String) &&
40
+ strategy.respond_to?(:enabled?)
41
+ @custom_strategy_implementations[strategy.name] = strategy
42
+ else
43
+ raise "Invalid strategy object. Must have a name method that returns a String and an enabled? method."
44
+ end
45
+ end
46
+ end
47
+
48
+ def evaluate_custom_strategies(toggle_name, context)
49
+ results = {}
50
+
51
+ @custom_strategies_definitions[toggle_name]&.each_with_index do |strategy, index|
52
+ key = "customStrategy#{index + 1}"
53
+ strategy_impl = @custom_strategy_implementations[strategy["name"]]
54
+ result = strategy_impl&.enabled?(strategy["parameters"], context) || false
55
+ results[key] = result
56
+ end
57
+
58
+ results
59
+ end
60
+ end
@@ -1,114 +1,114 @@
1
- require 'ffi'
2
- require 'json'
3
- require 'custom_strategy'
4
-
5
- TOGGLE_MISSING_RESPONSE = 'NotFound'.freeze
6
- ERROR_RESPONSE = 'Error'.freeze
7
- OK_RESPONSE = 'Ok'.freeze
8
-
9
- def platform_specific_lib
10
- case RbConfig::CONFIG['host_os']
11
- when /darwin|mac os/
12
- 'libyggdrasilffi.dylib'
13
- when /linux/
14
- 'libyggdrasilffi.so'
15
- when /mswin|msys|mingw|cygwin|bccwin|wince|emc/
16
- 'libyggdrasilffi.dll'
17
- else
18
- raise "unsupported platform #{RbConfig::CONFIG['host_os']}"
19
- end
20
- end
21
-
22
- def to_variant(raw_variant)
23
- payload = raw_variant[:payload] && raw_variant[:payload].transform_keys(&:to_s)
24
- {
25
- name: raw_variant[:name],
26
- enabled: raw_variant[:enabled],
27
- payload: payload,
28
- }
29
- end
30
-
31
- class YggdrasilEngine
32
- extend FFI::Library
33
- ffi_lib File.expand_path(platform_specific_lib, __dir__)
34
-
35
- attach_function :new_engine, [], :pointer
36
- attach_function :free_engine, [:pointer], :void
37
-
38
- attach_function :take_state, %i[pointer string], :pointer
39
- attach_function :check_enabled, %i[pointer string string string], :pointer
40
- attach_function :check_variant, %i[pointer string string string], :pointer
41
- attach_function :get_metrics, [:pointer], :pointer
42
- attach_function :free_response, [:pointer], :void
43
-
44
- attach_function :count_toggle, %i[pointer string bool], :void
45
- attach_function :count_variant, %i[pointer string string], :void
46
-
47
- def initialize
48
- @engine = YggdrasilEngine.new_engine
49
- @custom_strategy_handler = CustomStrategyHandler.new
50
- ObjectSpace.define_finalizer(self, self.class.finalize(@engine))
51
- end
52
-
53
- def self.finalize(engine)
54
- proc { YggdrasilEngine.free_engine(engine) }
55
- end
56
-
57
- def take_state(toggles)
58
- @custom_strategy_handler.update_strategies(toggles)
59
- response_ptr = YggdrasilEngine.take_state(@engine, toggles)
60
- take_toggles_response = JSON.parse(response_ptr.read_string, symbolize_names: true)
61
- YggdrasilEngine.free_response(response_ptr)
62
- end
63
-
64
- def get_variant(name, context)
65
- context_json = (context || {}).to_json
66
- custom_strategy_results = @custom_strategy_handler.evaluate_custom_strategies(name, context).to_json
67
-
68
- variant_def_json_ptr = YggdrasilEngine.check_variant(@engine, name, context_json, custom_strategy_results)
69
- variant_def_json = variant_def_json_ptr.read_string
70
- YggdrasilEngine.free_response(variant_def_json_ptr)
71
- variant_response = JSON.parse(variant_def_json, symbolize_names: true)
72
-
73
- return nil if variant_response[:status_code] == TOGGLE_MISSING_RESPONSE
74
- variant = variant_response[:value]
75
-
76
- return to_variant(variant) if variant_response[:status_code] == OK_RESPONSE
77
- end
78
-
79
- def enabled?(toggle_name, context)
80
- context_json = (context || {}).to_json
81
- custom_strategy_results = @custom_strategy_handler.evaluate_custom_strategies(toggle_name, context).to_json
82
-
83
- response_ptr = YggdrasilEngine.check_enabled(@engine, toggle_name, context_json, custom_strategy_results)
84
- response_json = response_ptr.read_string
85
- YggdrasilEngine.free_response(response_ptr)
86
- response = JSON.parse(response_json, symbolize_names: true)
87
-
88
- raise "Error: #{response[:error_message]}" if response[:status_code] == ERROR_RESPONSE
89
- return nil if response[:status_code] == TOGGLE_MISSING_RESPONSE
90
-
91
- return response[:value] == true
92
- end
93
-
94
- def count_toggle(toggle_name, enabled)
95
- response_ptr = YggdrasilEngine.count_toggle(@engine, toggle_name, enabled)
96
- YggdrasilEngine.free_response(response_ptr)
97
- end
98
-
99
- def count_variant(toggle_name, variant_name)
100
- response_ptr = YggdrasilEngine.count_variant(@engine, toggle_name, variant_name)
101
- YggdrasilEngine.free_response(response_ptr)
102
- end
103
-
104
- def get_metrics
105
- metrics_ptr = YggdrasilEngine.get_metrics(@engine)
106
- metrics = JSON.parse(metrics_ptr.read_string, symbolize_names: true)
107
- YggdrasilEngine.free_response(metrics_ptr)
108
- metrics[:value]
109
- end
110
-
111
- def register_custom_strategies(strategies)
112
- @custom_strategy_handler.register_custom_strategies(strategies)
113
- end
114
- end
1
+ require 'ffi'
2
+ require 'json'
3
+ require 'custom_strategy'
4
+
5
+ TOGGLE_MISSING_RESPONSE = 'NotFound'.freeze
6
+ ERROR_RESPONSE = 'Error'.freeze
7
+ OK_RESPONSE = 'Ok'.freeze
8
+
9
+ def platform_specific_lib
10
+ case RbConfig::CONFIG['host_os']
11
+ when /darwin|mac os/
12
+ 'libyggdrasilffi.dylib'
13
+ when /linux/
14
+ 'libyggdrasilffi.so'
15
+ when /mswin|msys|mingw|cygwin|bccwin|wince|emc/
16
+ 'libyggdrasilffi.dll'
17
+ else
18
+ raise "unsupported platform #{RbConfig::CONFIG['host_os']}"
19
+ end
20
+ end
21
+
22
+ def to_variant(raw_variant)
23
+ payload = raw_variant[:payload] && raw_variant[:payload].transform_keys(&:to_s)
24
+ {
25
+ name: raw_variant[:name],
26
+ enabled: raw_variant[:enabled],
27
+ payload: payload,
28
+ }
29
+ end
30
+
31
+ class YggdrasilEngine
32
+ extend FFI::Library
33
+ ffi_lib File.expand_path(platform_specific_lib, __dir__)
34
+
35
+ attach_function :new_engine, [], :pointer
36
+ attach_function :free_engine, [:pointer], :void
37
+
38
+ attach_function :take_state, %i[pointer string], :pointer
39
+ attach_function :check_enabled, %i[pointer string string string], :pointer
40
+ attach_function :check_variant, %i[pointer string string string], :pointer
41
+ attach_function :get_metrics, [:pointer], :pointer
42
+ attach_function :free_response, [:pointer], :void
43
+
44
+ attach_function :count_toggle, %i[pointer string bool], :void
45
+ attach_function :count_variant, %i[pointer string string], :void
46
+
47
+ def initialize
48
+ @engine = YggdrasilEngine.new_engine
49
+ @custom_strategy_handler = CustomStrategyHandler.new
50
+ ObjectSpace.define_finalizer(self, self.class.finalize(@engine))
51
+ end
52
+
53
+ def self.finalize(engine)
54
+ proc { YggdrasilEngine.free_engine(engine) }
55
+ end
56
+
57
+ def take_state(toggles)
58
+ @custom_strategy_handler.update_strategies(toggles)
59
+ response_ptr = YggdrasilEngine.take_state(@engine, toggles)
60
+ take_toggles_response = JSON.parse(response_ptr.read_string, symbolize_names: true)
61
+ YggdrasilEngine.free_response(response_ptr)
62
+ end
63
+
64
+ def get_variant(name, context)
65
+ context_json = (context || {}).to_json
66
+ custom_strategy_results = @custom_strategy_handler.evaluate_custom_strategies(name, context).to_json
67
+
68
+ variant_def_json_ptr = YggdrasilEngine.check_variant(@engine, name, context_json, custom_strategy_results)
69
+ variant_def_json = variant_def_json_ptr.read_string
70
+ YggdrasilEngine.free_response(variant_def_json_ptr)
71
+ variant_response = JSON.parse(variant_def_json, symbolize_names: true)
72
+
73
+ return nil if variant_response[:status_code] == TOGGLE_MISSING_RESPONSE
74
+ variant = variant_response[:value]
75
+
76
+ return to_variant(variant) if variant_response[:status_code] == OK_RESPONSE
77
+ end
78
+
79
+ def enabled?(toggle_name, context)
80
+ context_json = (context || {}).to_json
81
+ custom_strategy_results = @custom_strategy_handler.evaluate_custom_strategies(toggle_name, context).to_json
82
+
83
+ response_ptr = YggdrasilEngine.check_enabled(@engine, toggle_name, context_json, custom_strategy_results)
84
+ response_json = response_ptr.read_string
85
+ YggdrasilEngine.free_response(response_ptr)
86
+ response = JSON.parse(response_json, symbolize_names: true)
87
+
88
+ raise "Error: #{response[:error_message]}" if response[:status_code] == ERROR_RESPONSE
89
+ return nil if response[:status_code] == TOGGLE_MISSING_RESPONSE
90
+
91
+ return response[:value] == true
92
+ end
93
+
94
+ def count_toggle(toggle_name, enabled)
95
+ response_ptr = YggdrasilEngine.count_toggle(@engine, toggle_name, enabled)
96
+ YggdrasilEngine.free_response(response_ptr)
97
+ end
98
+
99
+ def count_variant(toggle_name, variant_name)
100
+ response_ptr = YggdrasilEngine.count_variant(@engine, toggle_name, variant_name)
101
+ YggdrasilEngine.free_response(response_ptr)
102
+ end
103
+
104
+ def get_metrics
105
+ metrics_ptr = YggdrasilEngine.get_metrics(@engine)
106
+ metrics = JSON.parse(metrics_ptr.read_string, symbolize_names: true)
107
+ YggdrasilEngine.free_response(metrics_ptr)
108
+ metrics[:value]
109
+ end
110
+
111
+ def register_custom_strategies(strategies)
112
+ @custom_strategy_handler.register_custom_strategies(strategies)
113
+ end
114
+ end
@@ -1,136 +1,136 @@
1
- require_relative '../lib/custom_strategy'
2
-
3
- RSpec.describe "custom strategies" do
4
- let(:raw_state) do
5
- {
6
- "version": 1,
7
- "features": [
8
- {
9
- "name": "Feature.A",
10
- "enabled": true,
11
- "strategies": [
12
- {
13
- "name": "default",
14
- "parameters": {}
15
- },
16
- {
17
- "name": "custom",
18
- "parameters": {
19
- "gerkhins": "yes"
20
- }
21
- },
22
- {
23
- "name": "some-other-custom",
24
- "parameters": {
25
- "gerkhins": "yes"
26
- }
27
- },
28
- ]
29
- }
30
- ]
31
- }
32
- end
33
-
34
- let(:handler) { CustomStrategyHandler.new }
35
-
36
- before do
37
- handler.update_strategies(raw_state.to_json)
38
- end
39
-
40
- describe 'computing custom strategies' do
41
- it 'respects the logic contained in the enabled function' do
42
- class TestStrategy
43
- attr_reader :name
44
-
45
- def initialize(name)
46
- @name = name
47
- end
48
-
49
- def enabled?(params, context)
50
- params["gerkhins"] == "yes"
51
- end
52
- end
53
-
54
- handler.register_custom_strategies([TestStrategy.new("custom")])
55
- strategy_results = handler.evaluate_custom_strategies("Feature.A", {})
56
- expect(strategy_results.length).to eq(2)
57
- expect(strategy_results["customStrategy1"]).to eq(true)
58
- expect(strategy_results["customStrategy2"]).to eq(false)
59
- end
60
-
61
- it 'creates a strategy result for every custom strategy thats implemented and defined' do
62
- class TestStrategy
63
- attr_reader :name
64
-
65
- def initialize(name)
66
- @name = name
67
- end
68
-
69
- def enabled?(params, context)
70
- params["gerkhins"] == "yes"
71
- end
72
- end
73
-
74
- handler.register_custom_strategies([TestStrategy.new("custom"), TestStrategy.new("some-other-custom")])
75
- strategy_results = handler.evaluate_custom_strategies("Feature.A", {})
76
- expect(strategy_results.length).to eq(2)
77
- expect(strategy_results["customStrategy1"]).to eq(true)
78
- expect(strategy_results["customStrategy2"]).to eq(true)
79
- end
80
-
81
- it 'returns false for missing implementations' do
82
- handler.register_custom_strategies([])
83
- strategy_results = handler.evaluate_custom_strategies("Feature.A", {})
84
- expect(strategy_results.length).to eq(2)
85
- expect(strategy_results["customStrategy1"]).to eq(false)
86
- end
87
-
88
- it "should calculate custom strategies e2e" do
89
- class TestStrategy
90
- attr_reader :name
91
-
92
- def initialize(name)
93
- @name = name
94
- end
95
-
96
- def enabled?(params, context)
97
- context[:userId] == "123"
98
- end
99
- end
100
-
101
- state = {
102
- "version": 1,
103
- "features": [
104
- {
105
- "name": "Feature.A",
106
- "enabled": true,
107
- "strategies": [
108
- {
109
- "name": "custom",
110
- "parameters": {
111
- "gerkhins": "yes"
112
- }
113
- }
114
- ]
115
- }
116
- ]
117
- }
118
-
119
- engine = YggdrasilEngine.new
120
- engine.register_custom_strategies([TestStrategy.new("custom")])
121
-
122
- engine.take_state(state.to_json)
123
-
124
- should_be_enabled = engine.enabled?("Feature.A", {
125
- userId: "123"
126
- })
127
-
128
- should_not_be_enabled = engine.enabled?("Feature.A", {
129
- userId: "456"
130
- })
131
-
132
- expect(should_be_enabled).to eq(true)
133
- expect(should_not_be_enabled).to eq(false)
134
- end
135
- end
136
- end
1
+ require_relative '../lib/custom_strategy'
2
+
3
+ RSpec.describe "custom strategies" do
4
+ let(:raw_state) do
5
+ {
6
+ "version": 1,
7
+ "features": [
8
+ {
9
+ "name": "Feature.A",
10
+ "enabled": true,
11
+ "strategies": [
12
+ {
13
+ "name": "default",
14
+ "parameters": {}
15
+ },
16
+ {
17
+ "name": "custom",
18
+ "parameters": {
19
+ "gerkhins": "yes"
20
+ }
21
+ },
22
+ {
23
+ "name": "some-other-custom",
24
+ "parameters": {
25
+ "gerkhins": "yes"
26
+ }
27
+ },
28
+ ]
29
+ }
30
+ ]
31
+ }
32
+ end
33
+
34
+ let(:handler) { CustomStrategyHandler.new }
35
+
36
+ before do
37
+ handler.update_strategies(raw_state.to_json)
38
+ end
39
+
40
+ describe 'computing custom strategies' do
41
+ it 'respects the logic contained in the enabled function' do
42
+ class TestStrategy
43
+ attr_reader :name
44
+
45
+ def initialize(name)
46
+ @name = name
47
+ end
48
+
49
+ def enabled?(params, context)
50
+ params["gerkhins"] == "yes"
51
+ end
52
+ end
53
+
54
+ handler.register_custom_strategies([TestStrategy.new("custom")])
55
+ strategy_results = handler.evaluate_custom_strategies("Feature.A", {})
56
+ expect(strategy_results.length).to eq(2)
57
+ expect(strategy_results["customStrategy1"]).to eq(true)
58
+ expect(strategy_results["customStrategy2"]).to eq(false)
59
+ end
60
+
61
+ it 'creates a strategy result for every custom strategy thats implemented and defined' do
62
+ class TestStrategy
63
+ attr_reader :name
64
+
65
+ def initialize(name)
66
+ @name = name
67
+ end
68
+
69
+ def enabled?(params, context)
70
+ params["gerkhins"] == "yes"
71
+ end
72
+ end
73
+
74
+ handler.register_custom_strategies([TestStrategy.new("custom"), TestStrategy.new("some-other-custom")])
75
+ strategy_results = handler.evaluate_custom_strategies("Feature.A", {})
76
+ expect(strategy_results.length).to eq(2)
77
+ expect(strategy_results["customStrategy1"]).to eq(true)
78
+ expect(strategy_results["customStrategy2"]).to eq(true)
79
+ end
80
+
81
+ it 'returns false for missing implementations' do
82
+ handler.register_custom_strategies([])
83
+ strategy_results = handler.evaluate_custom_strategies("Feature.A", {})
84
+ expect(strategy_results.length).to eq(2)
85
+ expect(strategy_results["customStrategy1"]).to eq(false)
86
+ end
87
+
88
+ it "should calculate custom strategies e2e" do
89
+ class TestStrategy
90
+ attr_reader :name
91
+
92
+ def initialize(name)
93
+ @name = name
94
+ end
95
+
96
+ def enabled?(params, context)
97
+ context[:userId] == "123"
98
+ end
99
+ end
100
+
101
+ state = {
102
+ "version": 1,
103
+ "features": [
104
+ {
105
+ "name": "Feature.A",
106
+ "enabled": true,
107
+ "strategies": [
108
+ {
109
+ "name": "custom",
110
+ "parameters": {
111
+ "gerkhins": "yes"
112
+ }
113
+ }
114
+ ]
115
+ }
116
+ ]
117
+ }
118
+
119
+ engine = YggdrasilEngine.new
120
+ engine.register_custom_strategies([TestStrategy.new("custom")])
121
+
122
+ engine.take_state(state.to_json)
123
+
124
+ should_be_enabled = engine.enabled?("Feature.A", {
125
+ userId: "123"
126
+ })
127
+
128
+ should_not_be_enabled = engine.enabled?("Feature.A", {
129
+ userId: "456"
130
+ })
131
+
132
+ expect(should_be_enabled).to eq(true)
133
+ expect(should_not_be_enabled).to eq(false)
134
+ end
135
+ end
136
+ end
@@ -1,139 +1,139 @@
1
- require 'rspec'
2
- require 'json'
3
- require_relative '../lib/yggdrasil_engine'
4
-
5
- index_file_path = '../client-specification/specifications/index.json'
6
- test_suites = JSON.parse(File.read(index_file_path))
7
-
8
- RSpec.describe YggdrasilEngine do
9
- let(:yggdrasil_engine) { YggdrasilEngine.new }
10
-
11
- describe '#checking a toggle' do
12
- it 'that does not exist should yield a not found' do
13
- is_enabled = yggdrasil_engine.enabled?('missing-toggle', {})
14
- expect(is_enabled).to be_nil
15
- end
16
- end
17
-
18
- describe '#metrics' do
19
- it 'should clear metrics when get_metrics is called' do
20
- feature_name = 'Feature.A'
21
-
22
- suite_path = File.join('../client-specification/specifications', '01-simple-examples.json')
23
- suite_data = JSON.parse(File.read(suite_path))
24
-
25
- yggdrasil_engine.take_state(suite_data['state'].to_json)
26
-
27
- yggdrasil_engine.count_toggle(feature_name, true)
28
- yggdrasil_engine.count_toggle(feature_name, false)
29
-
30
- metrics = yggdrasil_engine.get_metrics() # This should clear the metrics buffer
31
-
32
- metric = metrics[:toggles][feature_name.to_sym]
33
- expect(metric[:yes]).to eq(1)
34
- expect(metric[:no]).to eq(1)
35
-
36
- metrics = yggdrasil_engine.get_metrics()
37
- expect(metrics).to be_nil
38
- end
39
-
40
- it 'should increment toggle count when it exists' do
41
- toggle_name = 'Feature.A'
42
-
43
- suite_path = File.join('../client-specification/specifications', '01-simple-examples.json')
44
- suite_data = JSON.parse(File.read(suite_path))
45
-
46
- yggdrasil_engine.take_state(suite_data['state'].to_json)
47
-
48
- yggdrasil_engine.count_toggle(toggle_name, true)
49
- yggdrasil_engine.count_toggle(toggle_name, false)
50
-
51
- metrics = yggdrasil_engine.get_metrics()
52
- metric = metrics[:toggles][toggle_name.to_sym]
53
-
54
- expect(metric[:yes]).to eq(1)
55
- expect(metric[:no]).to eq(1)
56
- end
57
-
58
- it 'should increment toggle count when the toggle does not exist' do
59
- toggle_name = 'Feature.X'
60
-
61
- yggdrasil_engine.count_toggle(toggle_name, true)
62
- yggdrasil_engine.count_toggle(toggle_name, false)
63
-
64
- metrics = yggdrasil_engine.get_metrics()
65
- metric = metrics[:toggles][toggle_name.to_sym]
66
-
67
- expect(metric[:yes]).to eq(1)
68
- expect(metric[:no]).to eq(1)
69
- end
70
-
71
- it 'should increment variant' do
72
- toggle_name = 'Feature.Q'
73
-
74
- suite_path = File.join('../client-specification/specifications', '01-simple-examples.json')
75
- suite_data = JSON.parse(File.read(suite_path))
76
-
77
- yggdrasil_engine.take_state(suite_data['state'].to_json)
78
-
79
- yggdrasil_engine.count_variant(toggle_name, 'disabled')
80
-
81
- metrics = yggdrasil_engine.get_metrics()
82
- metric = metrics[:toggles][toggle_name.to_sym]
83
-
84
- expect(metric[:variants][:disabled]).to eq(1)
85
- end
86
- end
87
- end
88
-
89
- RSpec.describe 'Client Specification' do
90
- let(:yggdrasil_engine) { YggdrasilEngine.new }
91
-
92
- test_suites.each do |suite|
93
- suite_path = File.join('../client-specification/specifications', suite)
94
- suite_data = JSON.parse(File.read(suite_path), symbolize_names: true)
95
-
96
- describe "Suite '#{suite}'" do
97
- before(:each) do
98
- yggdrasil_engine.take_state(suite_data[:state].to_json)
99
- end
100
-
101
- suite_data.fetch(:tests, []).each do |test|
102
- describe "Test '#{test[:description]}'" do
103
- let(:context) { test[:context] }
104
- let(:toggle_name) { test[:toggleName] }
105
- let(:expected_result) { test[:expectedResult] }
106
-
107
- it 'returns correct result for `is_enabled?` method' do
108
- result = yggdrasil_engine.enabled?(toggle_name, context) || false
109
-
110
- expect(result).to eq(expected_result),
111
- "Failed test '#{test['description']}': expected #{expected_result}, got #{result}"
112
- end
113
- end
114
- end
115
-
116
- suite_data.fetch(:variantTests, []).each do |test|
117
- next unless test[:expectedResult]
118
-
119
- describe "Variant Test '#{test[:description]}'" do
120
- let(:context) { test[:context] }
121
- let(:toggle_name) { test[:toggleName] }
122
- let(:expected_result) { to_variant(test[:expectedResult]) }
123
-
124
- it 'returns correct result for `get_variant` method' do
125
- result = yggdrasil_engine.get_variant(toggle_name, context) || {
126
- :name => 'disabled',
127
- :payload => nil,
128
- :enabled => false
129
- }
130
-
131
- expect(result[:name]).to eq(expected_result[:name])
132
- expect(result[:payload]).to eq(expected_result[:payload])
133
- expect(result[:enabled]).to eq(expected_result[:enabled])
134
- end
135
- end
136
- end
137
- end
138
- end
139
- end
1
+ require 'rspec'
2
+ require 'json'
3
+ require_relative '../lib/yggdrasil_engine'
4
+
5
+ index_file_path = '../client-specification/specifications/index.json'
6
+ test_suites = JSON.parse(File.read(index_file_path))
7
+
8
+ RSpec.describe YggdrasilEngine do
9
+ let(:yggdrasil_engine) { YggdrasilEngine.new }
10
+
11
+ describe '#checking a toggle' do
12
+ it 'that does not exist should yield a not found' do
13
+ is_enabled = yggdrasil_engine.enabled?('missing-toggle', {})
14
+ expect(is_enabled).to be_nil
15
+ end
16
+ end
17
+
18
+ describe '#metrics' do
19
+ it 'should clear metrics when get_metrics is called' do
20
+ feature_name = 'Feature.A'
21
+
22
+ suite_path = File.join('../client-specification/specifications', '01-simple-examples.json')
23
+ suite_data = JSON.parse(File.read(suite_path))
24
+
25
+ yggdrasil_engine.take_state(suite_data['state'].to_json)
26
+
27
+ yggdrasil_engine.count_toggle(feature_name, true)
28
+ yggdrasil_engine.count_toggle(feature_name, false)
29
+
30
+ metrics = yggdrasil_engine.get_metrics() # This should clear the metrics buffer
31
+
32
+ metric = metrics[:toggles][feature_name.to_sym]
33
+ expect(metric[:yes]).to eq(1)
34
+ expect(metric[:no]).to eq(1)
35
+
36
+ metrics = yggdrasil_engine.get_metrics()
37
+ expect(metrics).to be_nil
38
+ end
39
+
40
+ it 'should increment toggle count when it exists' do
41
+ toggle_name = 'Feature.A'
42
+
43
+ suite_path = File.join('../client-specification/specifications', '01-simple-examples.json')
44
+ suite_data = JSON.parse(File.read(suite_path))
45
+
46
+ yggdrasil_engine.take_state(suite_data['state'].to_json)
47
+
48
+ yggdrasil_engine.count_toggle(toggle_name, true)
49
+ yggdrasil_engine.count_toggle(toggle_name, false)
50
+
51
+ metrics = yggdrasil_engine.get_metrics()
52
+ metric = metrics[:toggles][toggle_name.to_sym]
53
+
54
+ expect(metric[:yes]).to eq(1)
55
+ expect(metric[:no]).to eq(1)
56
+ end
57
+
58
+ it 'should increment toggle count when the toggle does not exist' do
59
+ toggle_name = 'Feature.X'
60
+
61
+ yggdrasil_engine.count_toggle(toggle_name, true)
62
+ yggdrasil_engine.count_toggle(toggle_name, false)
63
+
64
+ metrics = yggdrasil_engine.get_metrics()
65
+ metric = metrics[:toggles][toggle_name.to_sym]
66
+
67
+ expect(metric[:yes]).to eq(1)
68
+ expect(metric[:no]).to eq(1)
69
+ end
70
+
71
+ it 'should increment variant' do
72
+ toggle_name = 'Feature.Q'
73
+
74
+ suite_path = File.join('../client-specification/specifications', '01-simple-examples.json')
75
+ suite_data = JSON.parse(File.read(suite_path))
76
+
77
+ yggdrasil_engine.take_state(suite_data['state'].to_json)
78
+
79
+ yggdrasil_engine.count_variant(toggle_name, 'disabled')
80
+
81
+ metrics = yggdrasil_engine.get_metrics()
82
+ metric = metrics[:toggles][toggle_name.to_sym]
83
+
84
+ expect(metric[:variants][:disabled]).to eq(1)
85
+ end
86
+ end
87
+ end
88
+
89
+ RSpec.describe 'Client Specification' do
90
+ let(:yggdrasil_engine) { YggdrasilEngine.new }
91
+
92
+ test_suites.each do |suite|
93
+ suite_path = File.join('../client-specification/specifications', suite)
94
+ suite_data = JSON.parse(File.read(suite_path), symbolize_names: true)
95
+
96
+ describe "Suite '#{suite}'" do
97
+ before(:each) do
98
+ yggdrasil_engine.take_state(suite_data[:state].to_json)
99
+ end
100
+
101
+ suite_data.fetch(:tests, []).each do |test|
102
+ describe "Test '#{test[:description]}'" do
103
+ let(:context) { test[:context] }
104
+ let(:toggle_name) { test[:toggleName] }
105
+ let(:expected_result) { test[:expectedResult] }
106
+
107
+ it 'returns correct result for `is_enabled?` method' do
108
+ result = yggdrasil_engine.enabled?(toggle_name, context) || false
109
+
110
+ expect(result).to eq(expected_result),
111
+ "Failed test '#{test['description']}': expected #{expected_result}, got #{result}"
112
+ end
113
+ end
114
+ end
115
+
116
+ suite_data.fetch(:variantTests, []).each do |test|
117
+ next unless test[:expectedResult]
118
+
119
+ describe "Variant Test '#{test[:description]}'" do
120
+ let(:context) { test[:context] }
121
+ let(:toggle_name) { test[:toggleName] }
122
+ let(:expected_result) { to_variant(test[:expectedResult]) }
123
+
124
+ it 'returns correct result for `get_variant` method' do
125
+ result = yggdrasil_engine.get_variant(toggle_name, context) || {
126
+ :name => 'disabled',
127
+ :payload => nil,
128
+ :enabled => false
129
+ }
130
+
131
+ expect(result[:name]).to eq(expected_result[:name])
132
+ expect(result[:payload]).to eq(expected_result[:payload])
133
+ expect(result[:enabled]).to eq(expected_result[:enabled])
134
+ end
135
+ end
136
+ end
137
+ end
138
+ end
139
+ end
metadata CHANGED
@@ -1,11 +1,11 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: yggdrasil-engine
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3.beta.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Unleash
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
  date: 2023-06-28 00:00:00.000000000 Z
@@ -24,20 +24,6 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: 1.15.5
27
- - !ruby/object:Gem::Dependency
28
- name: fiddle
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - "~>"
32
- - !ruby/object:Gem::Version
33
- version: 1.0.6
34
- type: :runtime
35
- prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - "~>"
39
- - !ruby/object:Gem::Version
40
- version: 1.0.6
41
27
  description: "..."
42
28
  email: liquidwicked64@gmail.com
43
29
  executables: []
@@ -46,7 +32,7 @@ extra_rdoc_files: []
46
32
  files:
47
33
  - README.md
48
34
  - lib/custom_strategy.rb
49
- - lib/libyggdrasilffi.so
35
+ - lib/x86_64-pc-windows-gnu/yggdrasilffi.dll
50
36
  - lib/yggdrasil_engine.rb
51
37
  - spec/custom_strategy_spec.rb
52
38
  - spec/yggdrasil_engine_spec.rb
@@ -54,7 +40,7 @@ homepage: http://github.com/username/my_gem
54
40
  licenses:
55
41
  - MIT
56
42
  metadata: {}
57
- post_install_message:
43
+ post_install_message:
58
44
  rdoc_options: []
59
45
  require_paths:
60
46
  - lib
@@ -65,12 +51,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
65
51
  version: '0'
66
52
  required_rubygems_version: !ruby/object:Gem::Requirement
67
53
  requirements:
68
- - - ">="
54
+ - - ">"
69
55
  - !ruby/object:Gem::Version
70
- version: '0'
56
+ version: 1.3.1
71
57
  requirements: []
72
58
  rubygems_version: 3.2.33
73
- signing_key:
59
+ signing_key:
74
60
  specification_version: 4
75
61
  summary: Unleash engine for evaluating feature toggles
76
62
  test_files: []
Binary file