optimizely_server_side 0.0.5 → 0.0.6
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.
- 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
|

|
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
|