optimizely_server_side 0.0.2 → 0.0.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.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.rspec +2 -0
- data/.travis.yml +5 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +67 -0
- data/Readme.md +104 -0
- data/docs/screenshot.png +0 -0
- data/lib/optimizely_server_side/cache.rb +28 -0
- data/lib/optimizely_server_side/configuration.rb +16 -0
- data/lib/optimizely_server_side/datafile_fetcher.rb +19 -0
- data/lib/optimizely_server_side/helpers/support.rb +35 -0
- data/lib/optimizely_server_side/optimizely_sdk.rb +17 -0
- data/lib/optimizely_server_side/variation.rb +38 -0
- data/optimizely_server_side.gemspec +22 -0
- data/spec/optimizely_server_side/cache_spec.rb +40 -0
- data/spec/optimizely_server_side/configuration_spec.rb +21 -0
- data/spec/optimizely_server_side/datafile_fetcher_spec.rb +23 -0
- data/spec/optimizely_server_side/helpers/support_spec.rb +37 -0
- data/spec/optimizely_server_side/optimizely_sdk_spec.rb +113 -0
- data/spec/optimizely_server_side/variation_spec.rb +137 -0
- data/spec/optimizely_server_side_spec.rb +75 -0
- data/spec/spec_helper.rb +15 -0
- metadata +32 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6398f138ed81929b7eb9057466e0bb501f21cae3
|
4
|
+
data.tar.gz: abc2f4c80d0fa6dc094ed80dfc1395eb66e4685d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6591c7c1872081c2748074891cb9c1f4084fae529a752d2c78ae239b78790709b9fab4bd1ded08d1195116ffffcedc5fc452d68409620b6e9a98afc595a683b8
|
7
|
+
data.tar.gz: 75dd78eac9285bfd4bd988552e9fae740572af4916981ee0308e5e39abd6fcbed84b929ec355d0c752a2ca6a3ca7329b8966cc5abba42e0597899a293f118c02
|
data/.gitignore
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
.DS_Store
|
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
optimizely_server_side (0.0.1)
|
5
|
+
activesupport (~> 4.2, >= 4.2.6)
|
6
|
+
optimizely-sdk (~> 0.1.1)
|
7
|
+
|
8
|
+
GEM
|
9
|
+
remote: https://rubygems.org/
|
10
|
+
specs:
|
11
|
+
activesupport (4.2.7)
|
12
|
+
i18n (~> 0.7)
|
13
|
+
json (~> 1.7, >= 1.7.7)
|
14
|
+
minitest (~> 5.1)
|
15
|
+
thread_safe (~> 0.3, >= 0.3.4)
|
16
|
+
tzinfo (~> 1.1)
|
17
|
+
addressable (2.3.8)
|
18
|
+
crack (0.4.3)
|
19
|
+
safe_yaml (~> 1.0.0)
|
20
|
+
diff-lcs (1.2.5)
|
21
|
+
hashdiff (0.3.0)
|
22
|
+
httparty (0.13.7)
|
23
|
+
json (~> 1.8)
|
24
|
+
multi_xml (>= 0.5.2)
|
25
|
+
i18n (0.7.0)
|
26
|
+
json (1.8.3)
|
27
|
+
json-schema (2.6.2)
|
28
|
+
addressable (~> 2.3.8)
|
29
|
+
minitest (5.9.0)
|
30
|
+
multi_xml (0.5.5)
|
31
|
+
murmurhash3 (0.1.6)
|
32
|
+
optimizely-sdk (0.1.1)
|
33
|
+
httparty (~> 0.13.7)
|
34
|
+
json-schema (~> 2.6.2)
|
35
|
+
murmurhash3 (~> 0.1.6)
|
36
|
+
rspec (3.5.0)
|
37
|
+
rspec-core (~> 3.5.0)
|
38
|
+
rspec-expectations (~> 3.5.0)
|
39
|
+
rspec-mocks (~> 3.5.0)
|
40
|
+
rspec-core (3.5.2)
|
41
|
+
rspec-support (~> 3.5.0)
|
42
|
+
rspec-expectations (3.5.0)
|
43
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
44
|
+
rspec-support (~> 3.5.0)
|
45
|
+
rspec-mocks (3.5.0)
|
46
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
47
|
+
rspec-support (~> 3.5.0)
|
48
|
+
rspec-support (3.5.0)
|
49
|
+
safe_yaml (1.0.4)
|
50
|
+
thread_safe (0.3.5)
|
51
|
+
tzinfo (1.2.2)
|
52
|
+
thread_safe (~> 0.1)
|
53
|
+
webmock (2.1.0)
|
54
|
+
addressable (>= 2.3.6)
|
55
|
+
crack (>= 0.3.2)
|
56
|
+
hashdiff
|
57
|
+
|
58
|
+
PLATFORMS
|
59
|
+
ruby
|
60
|
+
|
61
|
+
DEPENDENCIES
|
62
|
+
optimizely_server_side!
|
63
|
+
rspec (~> 3.5)
|
64
|
+
webmock (~> 2.1)
|
65
|
+
|
66
|
+
BUNDLED WITH
|
67
|
+
1.12.5
|
data/Readme.md
ADDED
@@ -0,0 +1,104 @@
|
|
1
|
+
## Optimizely Server Side
|
2
|
+
|
3
|
+
[](https://codeclimate.com/github/ankit8898/optimizely_config_provider) [](https://travis-ci.org/ankit8898/optimizely_server_side)
|
4
|
+
|
5
|
+
### What is Optimizely Server Side ?
|
6
|
+
|
7
|
+
This is a wrapper on top of [Optimizely's](https://app.optimizely.com/projects) ruby sdk called [optimizely-sdk](https://github.com/optimizely/ruby-sdk) . The sdk specializes in server side setup of A/B test . You can read more about it [here](http://developers.optimizely.com/server/introduction/index.html) .
|
8
|
+
|
9
|
+
### If we have original sdk why need this wrapper ?
|
10
|
+
|
11
|
+
This gem solves few things:
|
12
|
+
|
13
|
+
- **Syncing AB test config across different servers when you don't want to fetch config via REST endpoint or redis/memcache store**
|
14
|
+
|
15
|
+
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.
|
16
|
+
|
17
|
+
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 file cached across different process.
|
18
|
+
|
19
|
+
The config is stored in **Memory Store** .
|
20
|
+
|
21
|
+
* **Some additional helpers**
|
22
|
+
|
23
|
+
Some more helpers exposed that can be exposed in views (.erbs) or PORO's. It avoids duplication of few activation settings.
|
24
|
+
|
25
|
+
### Getting Started
|
26
|
+
|
27
|
+
Add the gem in you Gemfile
|
28
|
+
|
29
|
+
```ruby
|
30
|
+
gem 'optimizely_server_side'
|
31
|
+
```
|
32
|
+
|
33
|
+
and
|
34
|
+
|
35
|
+
```ruby
|
36
|
+
bundle install
|
37
|
+
```
|
38
|
+
|
39
|
+
Add an initializer in `config/initializers/optimizely_server_side.rb`
|
40
|
+
|
41
|
+
```ruby
|
42
|
+
#config/initializers/optimizely_server_side.rb
|
43
|
+
OptimizelyServerSide.configure do |config|
|
44
|
+
config.config_endpoint = 'https://cdn.optimizely.com/json/PROJECT_ID.json'
|
45
|
+
config.cache_expiry = 15 #(this is in minutes)
|
46
|
+
end
|
47
|
+
|
48
|
+
```
|
49
|
+
`PROJECT_ID` is a id of your server side project at https://app.optimizely.com .
|
50
|
+
|
51
|
+
|
52
|
+
Optimizely needs a visitor_id to track the unique user and server a constant experience.
|
53
|
+
|
54
|
+
In your Application controller
|
55
|
+
|
56
|
+
```ruby
|
57
|
+
class ApplicationController < ActionController::Base
|
58
|
+
include OptimizelyServerSide::Support
|
59
|
+
|
60
|
+
before_action :set_visitor_id
|
61
|
+
|
62
|
+
def set_visitor_id
|
63
|
+
cookies.permanent[:visitor_id] = '1234567' #some visitor_id
|
64
|
+
|
65
|
+
# This links the browser cookie for visitor_id to
|
66
|
+
# OptimizelyServerSide
|
67
|
+
OptimizelyServerSide.configure do |config|
|
68
|
+
config.visitor_id = cookies[:visitor_id]
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
```
|
73
|
+
|
74
|
+
|
75
|
+
Now in your views or models
|
76
|
+
|
77
|
+
|
78
|
+
```ruby
|
79
|
+
experiment(EXPERIMENT_KEY) do |config|
|
80
|
+
|
81
|
+
config.variation_one(VARIATION_ONE_KEY) do
|
82
|
+
# Code for experience one. it can be html or a ruby code
|
83
|
+
end
|
84
|
+
|
85
|
+
config.variation_two(VARIATION_TWO_KEY) do
|
86
|
+
# Code for experience two. it can be html or a ruby code
|
87
|
+
end
|
88
|
+
|
89
|
+
config.variation_default(VARIATION_DEFAULT_KEY) do
|
90
|
+
# Code for experience default. it can be html or a ruby code
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
94
|
+
```
|
95
|
+
|
96
|
+
EXPERIMENT_KEY: The experiment key that you will be getting while setting up your experiment from https://app.optimizely.com.
|
97
|
+
|
98
|
+
VARIATION_ONE_KEY: Key for Variation one. This will be also set when setting up experiment
|
99
|
+
|
100
|
+
VARIATION_TWO_KEY: Key for Variation two. This will be also set when setting up experiment
|
101
|
+
|
102
|
+
VARIATION_DEFAULT_KEY: Key for default experience. This will be also set when setting up experiment
|
103
|
+
|
104
|
+

|
data/docs/screenshot.png
ADDED
Binary file
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module OptimizelyServerSide
|
2
|
+
|
3
|
+
# Maintains the API config response in Memory store cache.
|
4
|
+
# user Activesupport MemoryStore store.
|
5
|
+
class Cache
|
6
|
+
|
7
|
+
include Singleton
|
8
|
+
|
9
|
+
attr_reader :cache_store_instance
|
10
|
+
|
11
|
+
# We are sticking with Activesupprt memory store as gem is to be used with
|
12
|
+
# Rails app for now.
|
13
|
+
def initialize
|
14
|
+
@cache_store_instance = ActiveSupport::Cache::MemoryStore.new(expires_in: OptimizelyServerSide.configuration.cache_expiry.send(:minutes))
|
15
|
+
end
|
16
|
+
|
17
|
+
class << self
|
18
|
+
|
19
|
+
# fetch is a wrapper on top of Activesupport Fetch to set/get the
|
20
|
+
# response via singleton instance
|
21
|
+
def fetch(key)
|
22
|
+
instance.cache_store_instance.fetch(key) { yield }
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module OptimizelyServerSide
|
2
|
+
|
3
|
+
class Configuration
|
4
|
+
|
5
|
+
# Configuration enables to open up the configuration of gem for the application.
|
6
|
+
# config_endpoint: Optimizely config endpoint
|
7
|
+
# cache_expiry: (In minutes) How long we want to cache the config.
|
8
|
+
attr_accessor :config_endpoint, :cache_expiry, :visitor_id
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
@config_endpoint = 'http://foo.com'
|
12
|
+
@cache_expiry = 15
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module OptimizelyServerSide
|
2
|
+
|
3
|
+
class DatafileFetcher
|
4
|
+
# Responsible for fetching the optimizely sdk config from
|
5
|
+
# the API source. The API can be optimizely cdn itself or
|
6
|
+
# any other source.
|
7
|
+
|
8
|
+
class << self
|
9
|
+
|
10
|
+
# Fetch the Config from the specified source.
|
11
|
+
def fetch
|
12
|
+
Net::HTTP.get(URI(OptimizelyServerSide.configuration.config_endpoint))
|
13
|
+
end
|
14
|
+
alias_method :datafile, :fetch
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module OptimizelyServerSide
|
2
|
+
|
3
|
+
module Support
|
4
|
+
|
5
|
+
# Enables for us to wrap experiments
|
6
|
+
# Usage:
|
7
|
+
# experiment('sign_up_test') do |config|
|
8
|
+
#
|
9
|
+
# config.variation_one('variation_one_key') do
|
10
|
+
# # Code related to variation one
|
11
|
+
# end
|
12
|
+
#
|
13
|
+
# config.variation_two('variation_two_key') do
|
14
|
+
# # Code related to variation two
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# config.variation_default('variation_default_key') do
|
18
|
+
# # We still want to keep our default experience
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# end
|
22
|
+
|
23
|
+
def experiment(experiment_key, &blk)
|
24
|
+
result_variation_key = optimizely_sdk_project_instance(experiment_key)
|
25
|
+
variation_instance = OptimizelyServerSide::Variation.new(result_variation_key)
|
26
|
+
blk.call(variation_instance)
|
27
|
+
variation_instance.compute
|
28
|
+
end
|
29
|
+
|
30
|
+
def optimizely_sdk_project_instance(experiment_key)
|
31
|
+
OptimizelyServerSide::OptimizelySdk.project_instance(event_dispather: MyEventDispatcher.new).activate(experiment_key, OptimizelyServerSide.configuration.visitor_id)
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module OptimizelyServerSide
|
2
|
+
|
3
|
+
class OptimizelySdk
|
4
|
+
|
5
|
+
# Public method to be accessed in the application
|
6
|
+
# This is the project instance and is giving
|
7
|
+
# access to all the optimizely sdk methods.
|
8
|
+
# Datafile
|
9
|
+
def self.project_instance(options = {})
|
10
|
+
Cache.fetch('optimizely_sdk_config'.freeze) do
|
11
|
+
puts "Getting the configg"
|
12
|
+
Optimizely::Project.new(DatafileFetcher.datafile, options[:event_dispather])
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module OptimizelyServerSide
|
2
|
+
|
3
|
+
class Variation
|
4
|
+
|
5
|
+
attr_reader :hsh
|
6
|
+
|
7
|
+
def initialize(variation_key)
|
8
|
+
@variation_key = variation_key
|
9
|
+
@hsh = {}
|
10
|
+
end
|
11
|
+
|
12
|
+
# Variation one of experiment
|
13
|
+
def variation_one(key)
|
14
|
+
@hsh[key] = yield
|
15
|
+
end
|
16
|
+
|
17
|
+
# Variation two of experiment
|
18
|
+
def variation_two(key)
|
19
|
+
@hsh[key] = yield
|
20
|
+
end
|
21
|
+
|
22
|
+
def variation_default(key)
|
23
|
+
@hsh[key] = yield
|
24
|
+
end
|
25
|
+
|
26
|
+
# Variation three of experiment
|
27
|
+
def variation_three(key)
|
28
|
+
@hsh[key] = yield
|
29
|
+
end
|
30
|
+
|
31
|
+
# Select which variation to be picked up
|
32
|
+
def compute
|
33
|
+
@hsh.select do |key,value|
|
34
|
+
key == @variation_key
|
35
|
+
end.values[0]
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
$:.push File.expand_path("../lib", __FILE__)
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = 'optimizely_server_side'
|
5
|
+
s.version = '0.0.3'
|
6
|
+
s.date = '2016-08-10'
|
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 wrapper on top of optimizely's ruby sdk for easy caching of server side config and exposing few more utility helpers "
|
9
|
+
s.authors = ["Ankit Gupta"]
|
10
|
+
s.email = 'ankit.gupta8898@gmail.com'
|
11
|
+
s.files = `git ls-files`.split("\n")
|
12
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
13
|
+
|
14
|
+
s.require_paths = ["lib"]
|
15
|
+
s.homepage =
|
16
|
+
'https://github.com/ankit8898/optimizely_server_side'
|
17
|
+
s.license = 'MIT'
|
18
|
+
s.add_development_dependency 'rspec', '~> 3.5'
|
19
|
+
s.add_development_dependency 'webmock', '~> 2.1'
|
20
|
+
s.add_runtime_dependency 'optimizely-sdk' , '~> 0.1.1'
|
21
|
+
s.add_runtime_dependency 'activesupport', '~> 4.2', '>= 4.2.6'
|
22
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe OptimizelyServerSide::Cache do
|
4
|
+
|
5
|
+
describe 'singleton' do
|
6
|
+
|
7
|
+
it 'should be a singleton instance' do
|
8
|
+
expect(described_class.instance).to equal(described_class.instance)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
|
13
|
+
describe '#cache_store_instance' do
|
14
|
+
|
15
|
+
it 'should be a instance of Activesupport memory store' do
|
16
|
+
expect(described_class.instance.cache_store_instance).to be_kind_of(ActiveSupport::Cache::MemoryStore)
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
describe '.fetch' do
|
22
|
+
|
23
|
+
before do
|
24
|
+
stub_request(:get, "https://cdn.optimizely.com/json/5960232316.json")
|
25
|
+
.to_return(body: '{"experiments": [{"status": "running"}]}',status: 200)
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'should return the config from API and cache it' do
|
29
|
+
expect(
|
30
|
+
described_class.fetch('key') do
|
31
|
+
JSON.parse(OptimizelyServerSide::DatafileFetcher.datafile, symbolize_names: true)
|
32
|
+
end
|
33
|
+
).to eq(
|
34
|
+
{
|
35
|
+
experiments: [{status: "running"}]
|
36
|
+
}
|
37
|
+
)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe OptimizelyServerSide::Configuration do
|
4
|
+
|
5
|
+
subject { OptimizelyServerSide::Configuration.new }
|
6
|
+
|
7
|
+
describe '#config_endpoint' do
|
8
|
+
|
9
|
+
it 'has a default value of http://foo.com' do
|
10
|
+
expect(subject.config_endpoint).to eq('http://foo.com')
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
|
15
|
+
describe '#visitor_id' do
|
16
|
+
|
17
|
+
it 'defaults to nil' do
|
18
|
+
expect(subject.visitor_id).to be_nil
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe OptimizelyServerSide::DatafileFetcher do
|
4
|
+
|
5
|
+
describe '#fetch' do
|
6
|
+
|
7
|
+
before do
|
8
|
+
stub_request(:get, "https://cdn.optimizely.com/json/5960232316.json")
|
9
|
+
.to_return(body: '{"experiments": [{"status": "running"}]}',status: 200)
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'should fetch the config' do
|
13
|
+
expect(described_class.fetch).to eq('{"experiments": [{"status": "running"}]}')
|
14
|
+
end
|
15
|
+
|
16
|
+
|
17
|
+
it 'should return stringified datafile' do
|
18
|
+
expect(described_class.datafile).to eq('{"experiments": [{"status": "running"}]}')
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe OptimizelyServerSide::Support do
|
4
|
+
|
5
|
+
class FakeKlass
|
6
|
+
|
7
|
+
include OptimizelyServerSide::Support
|
8
|
+
|
9
|
+
|
10
|
+
def some_klass_method
|
11
|
+
|
12
|
+
experiment('foo_experiment_key') do |config|
|
13
|
+
|
14
|
+
config.variation_one('variation_one') do
|
15
|
+
'Experience one'
|
16
|
+
end
|
17
|
+
|
18
|
+
config.variation_two('variation_two') do
|
19
|
+
'Experience two'
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
|
27
|
+
context '#experiment' do
|
28
|
+
|
29
|
+
subject { FakeKlass.new }
|
30
|
+
|
31
|
+
before do
|
32
|
+
allow(subject).to receive(:optimizely_sdk_project_instance).and_return('variation_one')
|
33
|
+
end
|
34
|
+
|
35
|
+
it { expect(subject.some_klass_method).to eq('Experience one')}
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe OptimizelyServerSide::OptimizelySdk do
|
4
|
+
|
5
|
+
let(:valid_datafile) do
|
6
|
+
'{
|
7
|
+
"experiments": [{
|
8
|
+
"status": "Paused",
|
9
|
+
"percentageIncluded": 10000,
|
10
|
+
"key": "PROPERTY_HISTORY_TEST",
|
11
|
+
"trafficAllocation": [{
|
12
|
+
"entityId": "6038700472",
|
13
|
+
"endOfRange": 2500
|
14
|
+
}, {
|
15
|
+
"entityId": "6038700473",
|
16
|
+
"endOfRange": 7500
|
17
|
+
}, {
|
18
|
+
"entityId": "6038700474",
|
19
|
+
"endOfRange": 10000
|
20
|
+
}],
|
21
|
+
"audienceIds": [],
|
22
|
+
"variations": [{
|
23
|
+
"id": "6038700472",
|
24
|
+
"key": "PROPERTY_HISTORY_IMAGE_ONE"
|
25
|
+
}, {
|
26
|
+
"id": "6038700473",
|
27
|
+
"key": "PROPERTY_HISTORY_IMAGE_TWO"
|
28
|
+
}, {
|
29
|
+
"id": "6038700474",
|
30
|
+
"key": "DEFAULT"
|
31
|
+
}],
|
32
|
+
"forcedVariations": {},
|
33
|
+
"id": "6051271599"
|
34
|
+
}, {
|
35
|
+
"status": "Archived",
|
36
|
+
"percentageIncluded": 10000,
|
37
|
+
"key": "FS_SRP_MapPlusListView",
|
38
|
+
"trafficAllocation": [{
|
39
|
+
"entityId": "6236690179",
|
40
|
+
"endOfRange": 10000
|
41
|
+
}],
|
42
|
+
"audienceIds": [],
|
43
|
+
"variations": [{
|
44
|
+
"id": "6236690179",
|
45
|
+
"key": "MapViewPlusList"
|
46
|
+
}],
|
47
|
+
"forcedVariations": {},
|
48
|
+
"id": "6240360321"
|
49
|
+
}, {
|
50
|
+
"status": "Running",
|
51
|
+
"percentageIncluded": 10000,
|
52
|
+
"key": "MAPVERTISING",
|
53
|
+
"trafficAllocation": [{
|
54
|
+
"entityId": "6832840236",
|
55
|
+
"endOfRange": 3000
|
56
|
+
}, {
|
57
|
+
"entityId": "6832840237",
|
58
|
+
"endOfRange": 10000
|
59
|
+
}],
|
60
|
+
"audienceIds": [],
|
61
|
+
"variations": [{
|
62
|
+
"id": "6832840236",
|
63
|
+
"key": "VERSION_A"
|
64
|
+
}, {
|
65
|
+
"id": "6832840237",
|
66
|
+
"key": "VERSION_B"
|
67
|
+
}],
|
68
|
+
"forcedVariations": {},
|
69
|
+
"id": "6839510990"
|
70
|
+
}],
|
71
|
+
"version": "1",
|
72
|
+
"audiences": [],
|
73
|
+
"dimensions": [{
|
74
|
+
"id": "6202664479",
|
75
|
+
"key": "geography",
|
76
|
+
"segmentId": "6236461184"
|
77
|
+
}],
|
78
|
+
"groups": [],
|
79
|
+
"projectId": "5960232316",
|
80
|
+
"accountId": "5955320306",
|
81
|
+
"events": [{
|
82
|
+
"experimentIds": ["6051271599"],
|
83
|
+
"id": "6048790108",
|
84
|
+
"key": "Property history image check goal"
|
85
|
+
}, {
|
86
|
+
"experimentIds": ["6240360321"],
|
87
|
+
"id": "6231170981",
|
88
|
+
"key": "MapTest"
|
89
|
+
}, {
|
90
|
+
"experimentIds": ["6839510990"],
|
91
|
+
"id": "6813060279",
|
92
|
+
"key": "MAPVERTISING_GOAL"
|
93
|
+
}, {
|
94
|
+
"experimentIds": [],
|
95
|
+
"id": "5927811380",
|
96
|
+
"key": "Total Revenue"
|
97
|
+
}],
|
98
|
+
"revision": "23"
|
99
|
+
}'
|
100
|
+
end
|
101
|
+
|
102
|
+
describe '.project_instance' do
|
103
|
+
|
104
|
+
before do
|
105
|
+
stub_request(:get, "https://cdn.optimizely.com/json/5960232316.json")
|
106
|
+
.to_return(body: valid_datafile, status: 200)
|
107
|
+
end
|
108
|
+
|
109
|
+
it 'returns an OptimizelySdk project instance' do
|
110
|
+
expect(described_class.project_instance).to be_kind_of(Optimizely::Project)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,137 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe OptimizelyServerSide::Variation do
|
4
|
+
|
5
|
+
subject { OptimizelyServerSide::Variation.new(variation_key = 'variation_key_a') }
|
6
|
+
|
7
|
+
|
8
|
+
describe '#compute' do
|
9
|
+
|
10
|
+
|
11
|
+
before do
|
12
|
+
|
13
|
+
subject.variation_one('variation_key_a') do
|
14
|
+
'experience a'
|
15
|
+
end
|
16
|
+
|
17
|
+
subject.variation_two('variation_key_b') do
|
18
|
+
'experience b'
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'should result variation b' do
|
24
|
+
expect(subject.compute).to eq('experience a')
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
['variation_one','variation_two','variation_three','variation_default'].each do |variation|
|
30
|
+
|
31
|
+
describe "#{variation}" do
|
32
|
+
|
33
|
+
context 'it accepts regular strings' do
|
34
|
+
|
35
|
+
it do
|
36
|
+
expect(subject.send(variation,'foo') do
|
37
|
+
'Hello!'
|
38
|
+
end).to eq('Hello!')
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
context 'it accepts a block' do
|
45
|
+
|
46
|
+
let(:some_block) do
|
47
|
+
-> { 'something'}
|
48
|
+
end
|
49
|
+
|
50
|
+
it do
|
51
|
+
expect(subject.send(variation,'foo') do
|
52
|
+
some_block
|
53
|
+
end).to eq(some_block)
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
|
61
|
+
describe '#hsh' do
|
62
|
+
|
63
|
+
context 'key accepts regular strings' do
|
64
|
+
|
65
|
+
let(:string) { 'I am a variation' }
|
66
|
+
|
67
|
+
before do
|
68
|
+
subject.variation_one('foo') do
|
69
|
+
string
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'has value as string' do
|
74
|
+
expect(subject.hsh).to eq({'foo' => string})
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
|
79
|
+
|
80
|
+
context 'key accepts blocks / proc' do
|
81
|
+
|
82
|
+
let(:proc) { Proc.new {|n| n*2 } }
|
83
|
+
|
84
|
+
before do
|
85
|
+
subject.variation_one('foo') do
|
86
|
+
proc
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
it 'has value as proc' do
|
91
|
+
expect(subject.hsh).to eq({'foo' => proc})
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
95
|
+
|
96
|
+
context 'key accepts string, html or blocks / proc' do
|
97
|
+
|
98
|
+
let(:proc) { Proc.new {|n| n*2 } }
|
99
|
+
let(:html) do
|
100
|
+
'<!DOCTYPE html>
|
101
|
+
<html>
|
102
|
+
<head>
|
103
|
+
<title>Page Title</title>
|
104
|
+
</head>
|
105
|
+
<body>
|
106
|
+
|
107
|
+
<h1>This is a Heading</h1>
|
108
|
+
<p>This is a paragraph.</p>
|
109
|
+
|
110
|
+
</body>
|
111
|
+
</html>
|
112
|
+
'
|
113
|
+
end
|
114
|
+
let(:string) { 'Hello!'}
|
115
|
+
|
116
|
+
before do
|
117
|
+
subject.variation_one('foo') do
|
118
|
+
proc
|
119
|
+
end
|
120
|
+
|
121
|
+
subject.variation_two('foo_two') do
|
122
|
+
html
|
123
|
+
end
|
124
|
+
|
125
|
+
subject.variation_three('foo_three') do
|
126
|
+
string
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
it 'has value as proc' do
|
131
|
+
expect(subject.hsh).to eq({'foo' => proc, 'foo_two' => html, 'foo_three' => string})
|
132
|
+
end
|
133
|
+
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe OptimizelyServerSide do
|
4
|
+
|
5
|
+
describe "#configure" do
|
6
|
+
|
7
|
+
context 'when config is set in regular way' do
|
8
|
+
|
9
|
+
before do
|
10
|
+
OptimizelyServerSide.configure do |config|
|
11
|
+
config.config_endpoint = 'https://cdn.optimizely.com/json/5960232316.json'
|
12
|
+
config.cache_expiry = 12
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'has config_endpoint' do
|
17
|
+
expect(OptimizelyServerSide.configuration.config_endpoint).to eq('https://cdn.optimizely.com/json/5960232316.json')
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'has cache_expiry' do
|
21
|
+
expect(OptimizelyServerSide.configuration.cache_expiry).to eq(12)
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'has no visitor_id' do
|
25
|
+
expect(OptimizelyServerSide.configuration.visitor_id).to be_nil
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
context 'when config is set in between' do
|
31
|
+
|
32
|
+
before do
|
33
|
+
OptimizelyServerSide.configure do |config|
|
34
|
+
config.config_endpoint = 'https://cdn.optimizely.com/json/5960232316.json'
|
35
|
+
config.cache_expiry = 12
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'has config_endpoint' do
|
40
|
+
OptimizelyServerSide.configure do |config|
|
41
|
+
config.visitor_id = '1234abcdef'
|
42
|
+
end
|
43
|
+
|
44
|
+
expect(OptimizelyServerSide.configuration.config_endpoint).to eq('https://cdn.optimizely.com/json/5960232316.json')
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'has cache_expiry' do
|
48
|
+
expect(OptimizelyServerSide.configuration.cache_expiry).to eq(12)
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'has no visitor_id' do
|
52
|
+
expect(OptimizelyServerSide.configuration.visitor_id).to eq('1234abcdef')
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def foo
|
60
|
+
experiment(EXPERIMENT_KEY) do |config|
|
61
|
+
|
62
|
+
config.variation_one(VARIATION_ONE_KEY) do
|
63
|
+
# Code for experience one. it can be html or a ruby code
|
64
|
+
end
|
65
|
+
|
66
|
+
config.variation_two(VARIATION_TWO_KEY) do
|
67
|
+
# Code for experience two. it can be html or a ruby code
|
68
|
+
end
|
69
|
+
|
70
|
+
config.variation_default(variation_default_KEY) do
|
71
|
+
# Code for experience default. it can be html or a ruby code
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'bundler/setup'
|
2
|
+
require 'webmock/rspec'
|
3
|
+
Bundler.setup
|
4
|
+
|
5
|
+
require 'optimizely_server_side'
|
6
|
+
|
7
|
+
RSpec.configure do |config|
|
8
|
+
# some (optional) config here
|
9
|
+
|
10
|
+
config.before do
|
11
|
+
OptimizelyServerSide.configure do |config|
|
12
|
+
config.config_endpoint = 'https://cdn.optimizely.com/json/5960232316.json'
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
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.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ankit Gupta
|
@@ -79,7 +79,29 @@ executables: []
|
|
79
79
|
extensions: []
|
80
80
|
extra_rdoc_files: []
|
81
81
|
files:
|
82
|
+
- ".gitignore"
|
83
|
+
- ".rspec"
|
84
|
+
- ".travis.yml"
|
85
|
+
- Gemfile
|
86
|
+
- Gemfile.lock
|
87
|
+
- Readme.md
|
88
|
+
- docs/screenshot.png
|
82
89
|
- lib/optimizely_server_side.rb
|
90
|
+
- lib/optimizely_server_side/cache.rb
|
91
|
+
- lib/optimizely_server_side/configuration.rb
|
92
|
+
- lib/optimizely_server_side/datafile_fetcher.rb
|
93
|
+
- lib/optimizely_server_side/helpers/support.rb
|
94
|
+
- lib/optimizely_server_side/optimizely_sdk.rb
|
95
|
+
- lib/optimizely_server_side/variation.rb
|
96
|
+
- optimizely_server_side.gemspec
|
97
|
+
- spec/optimizely_server_side/cache_spec.rb
|
98
|
+
- spec/optimizely_server_side/configuration_spec.rb
|
99
|
+
- spec/optimizely_server_side/datafile_fetcher_spec.rb
|
100
|
+
- spec/optimizely_server_side/helpers/support_spec.rb
|
101
|
+
- spec/optimizely_server_side/optimizely_sdk_spec.rb
|
102
|
+
- spec/optimizely_server_side/variation_spec.rb
|
103
|
+
- spec/optimizely_server_side_spec.rb
|
104
|
+
- spec/spec_helper.rb
|
83
105
|
homepage: https://github.com/ankit8898/optimizely_server_side
|
84
106
|
licenses:
|
85
107
|
- MIT
|
@@ -105,4 +127,12 @@ signing_key:
|
|
105
127
|
specification_version: 4
|
106
128
|
summary: Optimizely server side. A wrapper on top of optimizely's ruby sdk for easy
|
107
129
|
caching of server side config
|
108
|
-
test_files:
|
130
|
+
test_files:
|
131
|
+
- spec/optimizely_server_side/cache_spec.rb
|
132
|
+
- spec/optimizely_server_side/configuration_spec.rb
|
133
|
+
- spec/optimizely_server_side/datafile_fetcher_spec.rb
|
134
|
+
- spec/optimizely_server_side/helpers/support_spec.rb
|
135
|
+
- spec/optimizely_server_side/optimizely_sdk_spec.rb
|
136
|
+
- spec/optimizely_server_side/variation_spec.rb
|
137
|
+
- spec/optimizely_server_side_spec.rb
|
138
|
+
- spec/spec_helper.rb
|