optimizely_server_side 0.0.5 → 0.0.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rspec +1 -1
- data/Gemfile.lock +4 -1
- data/Readme.md +76 -24
- data/lib/optimizely_server_side.rb +1 -0
- data/lib/optimizely_server_side/datafile_fetcher.rb +2 -1
- data/lib/optimizely_server_side/experiment.rb +40 -16
- data/lib/optimizely_server_side/helpers/support.rb +1 -2
- data/lib/optimizely_server_side/variation.rb +23 -0
- data/optimizely_server_side.gemspec +3 -2
- data/spec/optimizely_server_side/experiment_spec.rb +164 -16
- data/spec/optimizely_server_side/variation_spec.rb +75 -0
- data/spec/spec_helper.rb +2 -0
- metadata +27 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 52a8ee884cb6db767b45acb026984cb413f77dca
|
4
|
+
data.tar.gz: f674f904ce523f6003db14456367a47df57b48d2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 42058be44f0bd1cc8ce1a16b6aae1e7aba342d558a4cc5f8dacf13a97a03f5c3751126cf4ba10839cc89a0701026f32213af3e777345051aa3650c22dc851068
|
7
|
+
data.tar.gz: 115f4e2b2ee276c8eb29b5160fd83074fc12b25aa01f5fcdaab5eb3400c7fcafad738f7fce52781095a96855f218aa6207dc0c7aa63c22567ee0aed0e5614b2a
|
data/.rspec
CHANGED
@@ -1,2 +1,2 @@
|
|
1
|
-
--format
|
1
|
+
--format progress
|
2
2
|
--color
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
optimizely_server_side (0.0.
|
4
|
+
optimizely_server_side (0.0.6)
|
5
5
|
activesupport (~> 4.2, >= 4.2.6)
|
6
6
|
optimizely-sdk (~> 0.1.1)
|
7
7
|
|
@@ -40,6 +40,8 @@ GEM
|
|
40
40
|
rspec-core (~> 3.5.0)
|
41
41
|
rspec-expectations (~> 3.5.0)
|
42
42
|
rspec-mocks (~> 3.5.0)
|
43
|
+
rspec-collection_matchers (1.1.2)
|
44
|
+
rspec-expectations (>= 2.99.0.beta1)
|
43
45
|
rspec-core (3.5.2)
|
44
46
|
rspec-support (~> 3.5.0)
|
45
47
|
rspec-expectations (3.5.0)
|
@@ -70,6 +72,7 @@ DEPENDENCIES
|
|
70
72
|
codeclimate-test-reporter
|
71
73
|
optimizely_server_side!
|
72
74
|
rspec (~> 3.5)
|
75
|
+
rspec-collection_matchers (~> 1.1, >= 1.1.2)
|
73
76
|
webmock (~> 2.1)
|
74
77
|
|
75
78
|
BUNDLED WITH
|
data/Readme.md
CHANGED
@@ -12,19 +12,20 @@ This is a wrapper on top of [Optimizely's](https://app.optimizely.com/projects)
|
|
12
12
|
|
13
13
|
This gem solves few things:
|
14
14
|
|
15
|
-
- **Syncing
|
15
|
+
- **Syncing A/B test config across different servers when you don't want to fetch config via REST endpoint or redis/memcache store**
|
16
16
|
|
17
17
|
Yes, it's designed keeping performance in mind as we want to save a network overhead and a extra dependency.
|
18
18
|
|
19
19
|
If you are using Optimizely you will be aware about the [datafile](http://developers.optimizely.com/server/reference/index.html#datafile). Once we make changes to the A/B test like change in percent distribution, start / pause a experiment this file get's updated.
|
20
20
|
|
21
|
-
If you have 50 servers with 40 passenger / puma process these process needs to be updated. The Gem polls the config at regular interval and keeps the datafile cached across different process.
|
21
|
+
If you have 50 servers with 40 passenger / puma process each these process needs to be updated. The Gem polls the config at regular interval and keeps the datafile cached across different process.
|
22
22
|
|
23
23
|
The config is stored in **Memory Store** . We use [Activesupport memory store](http://api.rubyonrails.org/classes/ActiveSupport/Cache/MemoryStore.html) for same.
|
24
24
|
|
25
|
-
* **
|
25
|
+
* **Helper methods to better handle test and variations and handling fallbacks and experiment pause**
|
26
26
|
|
27
|
-
|
27
|
+
Optimizely ruby sdk provides us way to know which variation to show. But what happens when the experiment is paused ? Or there is a error happening in config.
|
28
|
+
More details about this in below section of experiment config.
|
28
29
|
|
29
30
|
### Architecture
|
30
31
|
|
@@ -58,12 +59,13 @@ end
|
|
58
59
|
`PROJECT_ID` is a id of your server side project at https://app.optimizely.com .
|
59
60
|
|
60
61
|
|
61
|
-
Optimizely needs a visitor_id to track the unique user and server a constant experience.
|
62
|
+
Optimizely needs a `visitor_id` to track the unique user and server a constant experience.
|
62
63
|
|
63
64
|
In your Application controller
|
64
65
|
|
65
66
|
```ruby
|
66
67
|
class ApplicationController < ActionController::Base
|
68
|
+
|
67
69
|
include OptimizelyServerSide::Support
|
68
70
|
|
69
71
|
before_action :set_visitor_id
|
@@ -80,48 +82,98 @@ class ApplicationController < ActionController::Base
|
|
80
82
|
|
81
83
|
```
|
82
84
|
|
83
|
-
|
85
|
+
### Example usage
|
84
86
|
|
87
|
+
#### In your html.erb
|
85
88
|
|
86
89
|
```ruby
|
87
|
-
|
90
|
+
# in any app/view/foo.html.erb
|
91
|
+
<% experiment(EXPERIMENT_KEY) do |config| %>
|
92
|
+
<% config.variation_one(VARIATION_ONE_KEY) do %>
|
93
|
+
<%= render partial: 'variation_one_experience'
|
94
|
+
<% end %>
|
95
|
+
|
96
|
+
<% config.variation_default(VARIATION_DEFAULT_KEY, primary: true) do %>
|
97
|
+
<%= render partial: 'variation_default_experience'
|
98
|
+
<% end %>
|
99
|
+
<% end %>
|
100
|
+
```
|
88
101
|
|
89
|
-
|
90
|
-
# Code for experience one. it can be html or a ruby code
|
91
|
-
end
|
102
|
+
#### In your model or any PORO
|
92
103
|
|
93
|
-
|
94
|
-
|
95
|
-
|
104
|
+
```ruby
|
105
|
+
class Foo
|
106
|
+
|
107
|
+
include OptimizelyServerSide::Support
|
96
108
|
|
97
|
-
config.variation_default(VARIATION_DEFAULT_KEY) do
|
98
|
-
# Code for experience default. it can be html or a ruby code
|
99
|
-
end
|
100
109
|
|
110
|
+
# This method is responsible from getting data from
|
111
|
+
# any other rest endpoint.
|
112
|
+
# Suppose you are doing a AB test on a new endpoint / data source.
|
113
|
+
def get_me_some_data
|
114
|
+
data = experiment(EXPERIMENT_KEY) do |config|
|
115
|
+
|
116
|
+
config.variation_one(VARIATION_ONE_KEY) do
|
117
|
+
HTTParty.get('http://from_source_a.com/users')
|
118
|
+
end
|
119
|
+
|
120
|
+
config.variation_default(VARIATION_TWO_KEY, primary: true) do
|
121
|
+
HTTParty.get('http://from_source_b.com/users')
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
end
|
101
126
|
end
|
102
127
|
```
|
103
128
|
|
104
|
-
|
129
|
+
In the above examples:
|
130
|
+
|
131
|
+
`EXPERIMENT_KEY`: When you will set your experiment this key will be set up that time at https://app.optimizely.com.
|
105
132
|
|
106
|
-
`VARIATION_ONE_KEY`: Key for Variation one. This will be also set when setting up experiment
|
133
|
+
`VARIATION_ONE_KEY`: Key for Variation one. This will be also set when setting up experiment.
|
107
134
|
|
108
|
-
`VARIATION_TWO_KEY`: Key for Variation two. This will be also set when setting up experiment
|
135
|
+
`VARIATION_TWO_KEY`: Key for Variation two. This will be also set when setting up experiment.
|
109
136
|
|
110
137
|
`VARIATION_DEFAULT_KEY`: Key for default experience. This will be also set when setting up experiment
|
111
138
|
|
139
|
+
`primary: true` : If you see above some variations are marked with `primary: true`. This enables handling the fallback capabilities of optimizely_server_side. If there is any error pulling datafile or experiment is paused the `primary` experience is served. Not setting primary won't give any experience during fallback times. We encourage setting it up.
|
140
|
+
|
112
141
|
![alt text](https://github.com/ankit8898/optimizely_server_side/blob/master/docs/screenshot.png "Logo Title Text 1")
|
113
142
|
|
114
|
-
### Testing
|
115
143
|
|
116
|
-
|
144
|
+
### Instrumentation
|
145
|
+
|
146
|
+
This is a trial feature and may or maynot exist in future version.
|
147
|
+
|
148
|
+
We have [ActiveSupport::Notifications](http://api.rubyonrails.org/classes/ActiveSupport/Notifications.html) hooked up for few places which are worth monitoring.
|
149
|
+
|
150
|
+
|
151
|
+
* When the datafile is fetched from cdn. In your application you can subscribe via below. This helps to monitor the time it takes from CDN fetch
|
117
152
|
|
118
153
|
```ruby
|
119
|
-
|
154
|
+
ActiveSupport::Notifications.subscribe "oss.call_optimizely_cdn" do |name, started, finished, unique_id, data|
|
155
|
+
Rails.logger.info "GET Datafile from Optimizely CDN in #{(finished - started) * 1000} ms"
|
156
|
+
end
|
120
157
|
```
|
158
|
+
* Which variation is being served currently. In your application you can subscribe via below
|
159
|
+
|
160
|
+
```ruby
|
121
161
|
|
162
|
+
ActiveSupport::Notifications.subscribe "oss.variation" do |name, started, finished, unique_id, data|
|
163
|
+
Rails.logger.info "GET Variation from OSS in #{(finished - started) * 1000} ms with variation key #{data[:variation]}"
|
164
|
+
end
|
122
165
|
```
|
123
|
-
|
124
|
-
|
166
|
+
### Testing
|
167
|
+
|
168
|
+
Gem uses rspec for unit testing
|
169
|
+
|
170
|
+
```ruby
|
171
|
+
$~/D/p/w/optimizely_server_side> rspec .
|
172
|
+
......................................................
|
173
|
+
|
174
|
+
Finished in 0.12234 seconds (files took 0.5512 seconds to load)
|
175
|
+
54 examples, 0 failures
|
176
|
+
|
125
177
|
```
|
126
178
|
|
127
179
|
### License
|
@@ -8,6 +8,7 @@ require 'optimizely_server_side/configuration'
|
|
8
8
|
require 'optimizely_server_side/datafile_fetcher'
|
9
9
|
require 'optimizely_server_side/experiment'
|
10
10
|
require 'optimizely_server_side/optimizely_sdk'
|
11
|
+
require 'optimizely_server_side/variation'
|
11
12
|
require 'optimizely_server_side/helpers/support'
|
12
13
|
|
13
14
|
module OptimizelyServerSide
|
@@ -8,7 +8,7 @@ module OptimizelyServerSide
|
|
8
8
|
|
9
9
|
attr_reader :content, :success
|
10
10
|
|
11
|
-
def initialize(content
|
11
|
+
def initialize(content: nil, success: false)
|
12
12
|
@content = content
|
13
13
|
@success = success
|
14
14
|
end
|
@@ -16,6 +16,7 @@ module OptimizelyServerSide
|
|
16
16
|
class << self
|
17
17
|
|
18
18
|
# Fetch the Config from the specified source.
|
19
|
+
# Incase of any error or exception we goto the fallback data
|
19
20
|
def fetch
|
20
21
|
begin
|
21
22
|
response = call_optimizely_cdn
|
@@ -2,36 +2,60 @@ module OptimizelyServerSide
|
|
2
2
|
class Experiment
|
3
3
|
|
4
4
|
def initialize(key)
|
5
|
-
@
|
6
|
-
@
|
5
|
+
@selected_variation_key = key
|
6
|
+
@variations = []
|
7
7
|
end
|
8
8
|
|
9
|
+
# Starts the experiment
|
9
10
|
def start
|
10
11
|
yield(self)
|
11
|
-
self.
|
12
|
+
self.applicable_variation
|
12
13
|
end
|
13
14
|
|
14
|
-
|
15
|
-
|
15
|
+
[
|
16
|
+
:variation_one,
|
17
|
+
:variation_two,
|
18
|
+
:variation_three,
|
19
|
+
:variation_default,
|
20
|
+
].each do |variation|
|
21
|
+
define_method(variation) do |key, opts={}, &blk| # def variation_one(key, opts = {}, &blk)
|
22
|
+
add_variation(key, opts, &blk) # add_variation(key, opts, &blk)
|
23
|
+
end # end
|
16
24
|
end
|
17
25
|
|
18
|
-
|
19
|
-
|
26
|
+
# Selects and calls the variation which is applicable
|
27
|
+
# In case of running test the applicable variation key is present
|
28
|
+
# In case of fallback / paused test we pick the primary variation
|
29
|
+
def applicable_variation
|
30
|
+
ActiveSupport::Notifications.instrument "oss.variation", variation: @another_key do
|
31
|
+
if @variations.any?(&variation_selector)
|
32
|
+
@variations.find(&variation_selector).call
|
33
|
+
else
|
34
|
+
primary_variation.call if primary_variation
|
35
|
+
end
|
36
|
+
end
|
20
37
|
end
|
21
38
|
|
22
|
-
|
23
|
-
|
39
|
+
# Primary variation is where primary: true
|
40
|
+
def primary_variation
|
41
|
+
@primary_variation ||= @variations.find(&:primary)
|
24
42
|
end
|
25
43
|
|
26
|
-
|
27
|
-
|
44
|
+
private
|
45
|
+
|
46
|
+
# Scope to query on selected variation
|
47
|
+
def variation_selector
|
48
|
+
->(variation) { variation.key == @selected_variation_key }
|
28
49
|
end
|
29
50
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
51
|
+
# Add all the variation to the variations collection
|
52
|
+
def add_variation(key, opts = {}, &blk)
|
53
|
+
Variation.new(
|
54
|
+
key: key,
|
55
|
+
primary: opts[:primary] || false,
|
56
|
+
content: blk
|
57
|
+
).tap do |variation_instance|
|
58
|
+
@variations << variation_instance
|
35
59
|
end
|
36
60
|
end
|
37
61
|
|
@@ -14,12 +14,11 @@ module OptimizelyServerSide
|
|
14
14
|
# # Code related to variation two
|
15
15
|
# end
|
16
16
|
#
|
17
|
-
# config.variation_default('variation_default_key') do
|
17
|
+
# config.variation_default('variation_default_key', primary: true) do
|
18
18
|
# # We still want to keep our default experience
|
19
19
|
# end
|
20
20
|
#
|
21
21
|
# end
|
22
|
-
|
23
22
|
def experiment(experiment_key, &blk)
|
24
23
|
variation_key = optimizely_sdk_project_instance(experiment_key)
|
25
24
|
OptimizelyServerSide::Experiment.new(variation_key).start(&blk)
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module OptimizelyServerSide
|
2
|
+
class Variation
|
3
|
+
|
4
|
+
# Class holding meta data about variation.
|
5
|
+
# content: The block / content of the variation
|
6
|
+
# Primary: If this is the primary variation. Primary is
|
7
|
+
# applicable in the cases of fallback / test is paused.
|
8
|
+
|
9
|
+
attr_reader :primary, :key
|
10
|
+
|
11
|
+
def initialize(primary: false, content: nil, key: nil)
|
12
|
+
@primary = primary
|
13
|
+
@key = key
|
14
|
+
@content = content
|
15
|
+
end
|
16
|
+
|
17
|
+
# Content is a block. Call is calling that block.
|
18
|
+
def call
|
19
|
+
@content.call
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
@@ -2,10 +2,10 @@ $:.push File.expand_path("../lib", __FILE__)
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = 'optimizely_server_side'
|
5
|
-
s.version = '0.0.
|
5
|
+
s.version = '0.0.6'
|
6
6
|
s.date = '2016-08-14'
|
7
7
|
s.summary = "Optimizely server side. A wrapper on top of optimizely's ruby sdk for easy caching of server side config "
|
8
|
-
s.description = "Optimizely server side. A
|
8
|
+
s.description = "Optimizely server side. A A/B test wrapper on top of optimizely's ruby sdk for easy caching of server side config and exposing few more utility helpers. Handling of fallbacks and marking primary experiments. "
|
9
9
|
s.authors = ["Ankit Gupta"]
|
10
10
|
s.email = 'ankit.gupta8898@gmail.com'
|
11
11
|
s.files = `git ls-files`.split("\n")
|
@@ -16,6 +16,7 @@ Gem::Specification.new do |s|
|
|
16
16
|
'https://github.com/ankit8898/optimizely_server_side'
|
17
17
|
s.license = 'MIT'
|
18
18
|
s.add_development_dependency 'rspec', '~> 3.5'
|
19
|
+
s.add_development_dependency 'rspec-collection_matchers', '~> 1.1', '>= 1.1.2'
|
19
20
|
s.add_development_dependency 'webmock', '~> 2.1'
|
20
21
|
s.add_runtime_dependency 'optimizely-sdk' , '~> 0.1.1'
|
21
22
|
s.add_runtime_dependency 'activesupport', '~> 4.2', '>= 4.2.6'
|
@@ -5,7 +5,7 @@ RSpec.describe OptimizelyServerSide::Experiment do
|
|
5
5
|
subject { OptimizelyServerSide::Experiment.new(variation_key = 'variation_key_a') }
|
6
6
|
|
7
7
|
|
8
|
-
describe '#
|
8
|
+
describe '#applicable_variation' do
|
9
9
|
|
10
10
|
before do
|
11
11
|
|
@@ -22,7 +22,7 @@ RSpec.describe OptimizelyServerSide::Experiment do
|
|
22
22
|
context 'when variation_key is present' do
|
23
23
|
|
24
24
|
it 'should result variation b' do
|
25
|
-
expect(subject.
|
25
|
+
expect(subject.applicable_variation).to eq('experience a')
|
26
26
|
end
|
27
27
|
|
28
28
|
end
|
@@ -32,7 +32,7 @@ RSpec.describe OptimizelyServerSide::Experiment do
|
|
32
32
|
subject { OptimizelyServerSide::Experiment.new(variation_key = '') }
|
33
33
|
|
34
34
|
it 'should be nil' do
|
35
|
-
expect(subject.
|
35
|
+
expect(subject.applicable_variation).to be_nil
|
36
36
|
end
|
37
37
|
end
|
38
38
|
|
@@ -43,9 +43,26 @@ RSpec.describe OptimizelyServerSide::Experiment do
|
|
43
43
|
|
44
44
|
let(:blk) { Proc.new { 'Hello!'} }
|
45
45
|
|
46
|
-
|
47
|
-
|
46
|
+
before do
|
47
|
+
@variation_instance = subject.variation_one('foo', &blk)
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'returns a variation instance passed' do
|
51
|
+
expect(@variation_instance).to be_kind_of(OptimizelyServerSide::Variation)
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'returns a key of foo' do
|
55
|
+
expect(@variation_instance.key).to eq('foo')
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'returns a content with blk ' do
|
59
|
+
expect(@variation_instance.instance_variable_get(:@content)).to eq(blk)
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'is not primary' do
|
63
|
+
expect(@variation_instance.primary).to be(false)
|
48
64
|
end
|
65
|
+
|
49
66
|
end
|
50
67
|
|
51
68
|
|
@@ -53,8 +70,24 @@ RSpec.describe OptimizelyServerSide::Experiment do
|
|
53
70
|
|
54
71
|
let(:blk) { -> {OpenStruct.new } }
|
55
72
|
|
56
|
-
|
57
|
-
|
73
|
+
before do
|
74
|
+
@variation_instance = subject.variation_two('foo', &blk)
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'returns a variation instance passed' do
|
78
|
+
expect(@variation_instance).to be_kind_of(OptimizelyServerSide::Variation)
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'returns a key of foo' do
|
82
|
+
expect(@variation_instance.key).to eq('foo')
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'returns a content with blk ' do
|
86
|
+
expect(@variation_instance.instance_variable_get(:@content)).to eq(blk)
|
87
|
+
end
|
88
|
+
|
89
|
+
it 'is not primary' do
|
90
|
+
expect(@variation_instance.primary).to be(false)
|
58
91
|
end
|
59
92
|
end
|
60
93
|
|
@@ -62,12 +95,53 @@ RSpec.describe OptimizelyServerSide::Experiment do
|
|
62
95
|
|
63
96
|
let(:blk) { Proc.new { 'Hello!'} }
|
64
97
|
|
65
|
-
|
66
|
-
|
98
|
+
before do
|
99
|
+
@variation_instance = subject.variation_three('foo', &blk)
|
100
|
+
end
|
101
|
+
|
102
|
+
it 'returns a variation instance passed' do
|
103
|
+
expect(@variation_instance).to be_kind_of(OptimizelyServerSide::Variation)
|
104
|
+
end
|
105
|
+
|
106
|
+
it 'returns a key of foo' do
|
107
|
+
expect(@variation_instance.key).to eq('foo')
|
108
|
+
end
|
109
|
+
|
110
|
+
it 'returns a content with blk ' do
|
111
|
+
expect(@variation_instance.instance_variable_get(:@content)).to eq(blk)
|
112
|
+
end
|
113
|
+
|
114
|
+
it 'is not primary' do
|
115
|
+
expect(@variation_instance.primary).to be(false)
|
67
116
|
end
|
68
117
|
end
|
69
118
|
|
70
|
-
describe '#
|
119
|
+
describe '#variation_default' do
|
120
|
+
|
121
|
+
let(:blk) { -> {'<div><h1>Hello</h1></div>'} }
|
122
|
+
|
123
|
+
before do
|
124
|
+
@variation_instance = subject.variation_default('foo', primary: true, &blk)
|
125
|
+
end
|
126
|
+
|
127
|
+
it 'returns a variation instance passed' do
|
128
|
+
expect(@variation_instance).to be_kind_of(OptimizelyServerSide::Variation)
|
129
|
+
end
|
130
|
+
|
131
|
+
it 'returns a key of foo' do
|
132
|
+
expect(@variation_instance.key).to eq('foo')
|
133
|
+
end
|
134
|
+
|
135
|
+
it 'returns a content with blk ' do
|
136
|
+
expect(@variation_instance.instance_variable_get(:@content)).to eq(blk)
|
137
|
+
end
|
138
|
+
|
139
|
+
it 'is primary' do
|
140
|
+
expect(@variation_instance.primary).to be(true)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
describe '#variations' do
|
71
145
|
|
72
146
|
context 'key accepts regular strings' do
|
73
147
|
|
@@ -77,8 +151,16 @@ RSpec.describe OptimizelyServerSide::Experiment do
|
|
77
151
|
subject.variation_one('foo', &string_lambda)
|
78
152
|
end
|
79
153
|
|
80
|
-
it '
|
81
|
-
expect(subject.instance_variable_get(:@
|
154
|
+
it 'holds collection of variations' do
|
155
|
+
expect(subject.instance_variable_get(:@variations)).to have(1).items
|
156
|
+
end
|
157
|
+
|
158
|
+
it 'is a type of Variation' do
|
159
|
+
expect(subject.instance_variable_get(:@variations)[0]).to be_kind_of(OptimizelyServerSide::Variation)
|
160
|
+
end
|
161
|
+
|
162
|
+
it 'is having the block content' do
|
163
|
+
expect(subject.instance_variable_get(:@variations)[0].call).to eq('I am a variation')
|
82
164
|
end
|
83
165
|
|
84
166
|
end
|
@@ -92,8 +174,12 @@ RSpec.describe OptimizelyServerSide::Experiment do
|
|
92
174
|
subject.variation_one('foo', &some_method)
|
93
175
|
end
|
94
176
|
|
95
|
-
it 'has
|
96
|
-
expect(subject.instance_variable_get(:@
|
177
|
+
it 'has content as proc' do
|
178
|
+
expect(subject.instance_variable_get(:@variations)[0].instance_variable_get(:@content)).to eq(some_method)
|
179
|
+
end
|
180
|
+
|
181
|
+
it 'has key' do
|
182
|
+
expect(subject.instance_variable_get(:@variations)[0].key).to eq('foo')
|
97
183
|
end
|
98
184
|
|
99
185
|
end
|
@@ -129,10 +215,72 @@ RSpec.describe OptimizelyServerSide::Experiment do
|
|
129
215
|
subject.variation_three('foo_three', &string_blk)
|
130
216
|
end
|
131
217
|
|
132
|
-
it 'has
|
133
|
-
expect(subject.instance_variable_get(:@
|
218
|
+
it 'has all variations' do
|
219
|
+
expect(subject.instance_variable_get(:@variations)).to have(3).items
|
220
|
+
end
|
221
|
+
|
222
|
+
it 'has all variations of class Variation' do
|
223
|
+
expect(subject.instance_variable_get(:@variations)).to all(be_an(OptimizelyServerSide::Variation))
|
224
|
+
end
|
225
|
+
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
|
230
|
+
describe '#primary_variation' do
|
231
|
+
|
232
|
+
let(:some_method) { Proc.new {|n| n*2 } }
|
233
|
+
|
234
|
+
let(:some_html_block) do
|
235
|
+
-> { '<h1>Foo</h1>' }
|
236
|
+
end
|
237
|
+
|
238
|
+
let(:string_blk) { -> { 'Hello!'} }
|
239
|
+
|
240
|
+
|
241
|
+
context 'when nothing is marked as primary' do
|
242
|
+
|
243
|
+
before do
|
244
|
+
subject.variation_one('foo', &some_method)
|
245
|
+
|
246
|
+
subject.variation_two('foo_two', &some_html_block)
|
247
|
+
|
248
|
+
subject.variation_three('foo_three', &string_blk)
|
249
|
+
end
|
250
|
+
|
251
|
+
it { expect(subject.primary_variation).to be_nil }
|
252
|
+
end
|
253
|
+
|
254
|
+
context 'when one is marked as primary' do
|
255
|
+
|
256
|
+
before do
|
257
|
+
subject.variation_one('foo', &some_method)
|
258
|
+
|
259
|
+
subject.variation_two('foo_two',primary: true, &some_html_block)
|
260
|
+
|
261
|
+
subject.variation_three('foo_three', &string_blk)
|
134
262
|
end
|
135
263
|
|
264
|
+
it { expect(subject.primary_variation).not_to be_nil }
|
265
|
+
|
266
|
+
it { expect(subject.primary_variation.call).to eq('<h1>Foo</h1>') }
|
136
267
|
end
|
268
|
+
|
269
|
+
|
270
|
+
context 'when multiple are marked as primary' do
|
271
|
+
|
272
|
+
before do
|
273
|
+
subject.variation_one('foo', &some_method)
|
274
|
+
|
275
|
+
subject.variation_three('foo_two', primary: true, &some_html_block)
|
276
|
+
|
277
|
+
subject.variation_two('foo_three',primary: true, &string_blk)
|
278
|
+
end
|
279
|
+
|
280
|
+
it { expect(subject.primary_variation).not_to be_nil }
|
281
|
+
|
282
|
+
it { expect(subject.primary_variation.call).to eq('<h1>Foo</h1>') }
|
283
|
+
end
|
284
|
+
|
137
285
|
end
|
138
286
|
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe OptimizelyServerSide::Variation do
|
4
|
+
|
5
|
+
describe '#primary' do
|
6
|
+
|
7
|
+
context 'when no primary is passed' do
|
8
|
+
|
9
|
+
subject { described_class.new }
|
10
|
+
|
11
|
+
it 'defaults to false' do
|
12
|
+
expect(subject.primary).to be(false)
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
context 'when primary is passed' do
|
18
|
+
|
19
|
+
subject { described_class.new(primary: true) }
|
20
|
+
|
21
|
+
it 'is as per passed' do
|
22
|
+
expect(subject.primary).to be(true)
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
describe '#content' do
|
30
|
+
|
31
|
+
context 'when no content is passed' do
|
32
|
+
|
33
|
+
subject { described_class.new }
|
34
|
+
|
35
|
+
it 'defaults to nil' do
|
36
|
+
expect(subject.instance_variable_get(:@content)).to be_nil
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
context 'when content is passed' do
|
42
|
+
|
43
|
+
let(:some_proc) { Proc.new { |n| "Hello #{n} world!"} }
|
44
|
+
|
45
|
+
subject { described_class.new(content: some_proc) }
|
46
|
+
|
47
|
+
it 'is as per passed' do
|
48
|
+
expect(subject.instance_variable_get(:@content)).to be(some_proc)
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
describe '#key' do
|
55
|
+
|
56
|
+
context 'when no key is passed' do
|
57
|
+
|
58
|
+
subject { described_class.new }
|
59
|
+
|
60
|
+
it 'defaults to nil' do
|
61
|
+
expect(subject.instance_variable_get(:@key)).to be_nil
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
context 'when key is passed' do
|
66
|
+
|
67
|
+
|
68
|
+
subject { described_class.new(key: 'ankit_test') }
|
69
|
+
|
70
|
+
it 'is as per passed' do
|
71
|
+
expect(subject.instance_variable_get(:@key)).to eq('ankit_test')
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: optimizely_server_side
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ankit Gupta
|
@@ -24,6 +24,26 @@ dependencies:
|
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '3.5'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rspec-collection_matchers
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.1'
|
34
|
+
- - ">="
|
35
|
+
- !ruby/object:Gem::Version
|
36
|
+
version: 1.1.2
|
37
|
+
type: :development
|
38
|
+
prerelease: false
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - "~>"
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '1.1'
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: 1.1.2
|
27
47
|
- !ruby/object:Gem::Dependency
|
28
48
|
name: webmock
|
29
49
|
requirement: !ruby/object:Gem::Requirement
|
@@ -72,9 +92,9 @@ dependencies:
|
|
72
92
|
- - ">="
|
73
93
|
- !ruby/object:Gem::Version
|
74
94
|
version: 4.2.6
|
75
|
-
description: 'Optimizely server side. A
|
76
|
-
sdk for easy caching of server side config and exposing few more utility helpers.
|
77
|
-
of fallbacks and marking primary experiments. '
|
95
|
+
description: 'Optimizely server side. A A/B test wrapper on top of optimizely''s ruby
|
96
|
+
sdk for easy caching of server side config and exposing few more utility helpers.
|
97
|
+
Handling of fallbacks and marking primary experiments. '
|
78
98
|
email: ankit.gupta8898@gmail.com
|
79
99
|
executables: []
|
80
100
|
extensions: []
|
@@ -96,6 +116,7 @@ files:
|
|
96
116
|
- lib/optimizely_server_side/experiment.rb
|
97
117
|
- lib/optimizely_server_side/helpers/support.rb
|
98
118
|
- lib/optimizely_server_side/optimizely_sdk.rb
|
119
|
+
- lib/optimizely_server_side/variation.rb
|
99
120
|
- optimizely_server_side.gemspec
|
100
121
|
- spec/optimizely_server_side/cache_spec.rb
|
101
122
|
- spec/optimizely_server_side/configuration_spec.rb
|
@@ -103,6 +124,7 @@ files:
|
|
103
124
|
- spec/optimizely_server_side/experiment_spec.rb
|
104
125
|
- spec/optimizely_server_side/helpers/support_spec.rb
|
105
126
|
- spec/optimizely_server_side/optimizely_sdk_spec.rb
|
127
|
+
- spec/optimizely_server_side/variation_spec.rb
|
106
128
|
- spec/optimizely_server_side_spec.rb
|
107
129
|
- spec/spec_helper.rb
|
108
130
|
homepage: https://github.com/ankit8898/optimizely_server_side
|
@@ -137,5 +159,6 @@ test_files:
|
|
137
159
|
- spec/optimizely_server_side/experiment_spec.rb
|
138
160
|
- spec/optimizely_server_side/helpers/support_spec.rb
|
139
161
|
- spec/optimizely_server_side/optimizely_sdk_spec.rb
|
162
|
+
- spec/optimizely_server_side/variation_spec.rb
|
140
163
|
- spec/optimizely_server_side_spec.rb
|
141
164
|
- spec/spec_helper.rb
|