split 4.0.1 → 4.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/.github/workflows/ci.yml +8 -3
- data/.rubocop.yml +2 -5
- data/CHANGELOG.md +38 -0
- data/CONTRIBUTING.md +1 -1
- data/Gemfile +1 -1
- data/README.md +11 -3
- data/Rakefile +4 -5
- data/gemfiles/5.2.gemfile +1 -3
- data/gemfiles/6.0.gemfile +1 -3
- data/gemfiles/6.1.gemfile +1 -3
- data/gemfiles/7.0.gemfile +1 -3
- data/lib/split/algorithms/block_randomization.rb +5 -6
- data/lib/split/algorithms/whiplash.rb +16 -18
- data/lib/split/algorithms.rb +14 -0
- data/lib/split/alternative.rb +21 -22
- data/lib/split/cache.rb +0 -1
- data/lib/split/combined_experiments_helper.rb +4 -4
- data/lib/split/configuration.rb +83 -84
- data/lib/split/dashboard/helpers.rb +6 -7
- data/lib/split/dashboard/pagination_helpers.rb +53 -54
- data/lib/split/dashboard/public/style.css +5 -2
- data/lib/split/dashboard/views/_experiment.erb +2 -1
- data/lib/split/dashboard/views/index.erb +19 -4
- data/lib/split/dashboard.rb +29 -23
- data/lib/split/encapsulated_helper.rb +4 -6
- data/lib/split/experiment.rb +93 -88
- data/lib/split/experiment_catalog.rb +6 -5
- data/lib/split/extensions/string.rb +1 -1
- data/lib/split/goals_collection.rb +8 -10
- data/lib/split/helper.rb +20 -20
- data/lib/split/metric.rb +4 -5
- data/lib/split/persistence/cookie_adapter.rb +44 -47
- data/lib/split/persistence/dual_adapter.rb +7 -8
- data/lib/split/persistence/redis_adapter.rb +3 -4
- data/lib/split/persistence/session_adapter.rb +0 -2
- data/lib/split/persistence.rb +4 -4
- data/lib/split/redis_interface.rb +7 -1
- data/lib/split/trial.rb +23 -24
- data/lib/split/user.rb +12 -13
- data/lib/split/version.rb +1 -1
- data/lib/split/zscore.rb +1 -3
- data/lib/split.rb +26 -25
- data/spec/algorithms/block_randomization_spec.rb +6 -5
- data/spec/algorithms/weighted_sample_spec.rb +6 -5
- data/spec/algorithms/whiplash_spec.rb +4 -5
- data/spec/alternative_spec.rb +35 -36
- data/spec/cache_spec.rb +15 -19
- data/spec/combined_experiments_helper_spec.rb +18 -17
- data/spec/configuration_spec.rb +32 -38
- data/spec/dashboard/pagination_helpers_spec.rb +69 -67
- data/spec/dashboard/paginator_spec.rb +10 -9
- data/spec/dashboard_helpers_spec.rb +19 -18
- data/spec/dashboard_spec.rb +79 -35
- data/spec/encapsulated_helper_spec.rb +12 -14
- data/spec/experiment_catalog_spec.rb +14 -13
- data/spec/experiment_spec.rb +132 -123
- data/spec/goals_collection_spec.rb +17 -15
- data/spec/helper_spec.rb +415 -382
- data/spec/metric_spec.rb +14 -14
- data/spec/persistence/cookie_adapter_spec.rb +23 -8
- data/spec/persistence/dual_adapter_spec.rb +71 -71
- data/spec/persistence/redis_adapter_spec.rb +28 -29
- data/spec/persistence/session_adapter_spec.rb +2 -3
- data/spec/persistence_spec.rb +1 -2
- data/spec/redis_interface_spec.rb +26 -14
- data/spec/spec_helper.rb +16 -13
- data/spec/split_spec.rb +11 -11
- data/spec/support/cookies_mock.rb +1 -2
- data/spec/trial_spec.rb +61 -60
- data/spec/user_spec.rb +36 -36
- data/split.gemspec +21 -20
- metadata +25 -14
- data/.rubocop_todo.yml +0 -226
- data/Appraisals +0 -19
- data/gemfiles/5.0.gemfile +0 -9
- data/gemfiles/5.1.gemfile +0 -9
    
        data/spec/alternative_spec.rb
    CHANGED
    
    | @@ -1,19 +1,19 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 | 
            -
            require 'spec_helper'
         | 
| 3 | 
            -
            require 'split/alternative'
         | 
| 4 2 |  | 
| 5 | 
            -
             | 
| 3 | 
            +
            require "spec_helper"
         | 
| 4 | 
            +
            require "split/alternative"
         | 
| 6 5 |  | 
| 6 | 
            +
            describe Split::Alternative do
         | 
| 7 7 | 
             
              let(:alternative) {
         | 
| 8 | 
            -
                Split::Alternative.new( | 
| 8 | 
            +
                Split::Alternative.new("Basket", "basket_text")
         | 
| 9 9 | 
             
              }
         | 
| 10 10 |  | 
| 11 11 | 
             
              let(:alternative2) {
         | 
| 12 | 
            -
                Split::Alternative.new( | 
| 12 | 
            +
                Split::Alternative.new("Cart", "basket_text")
         | 
| 13 13 | 
             
              }
         | 
| 14 14 |  | 
| 15 15 | 
             
              let!(:experiment) {
         | 
| 16 | 
            -
                Split::ExperimentCatalog.find_or_create({"basket_text" => ["purchase", "refund"]}, "Basket", "Cart")
         | 
| 16 | 
            +
                Split::ExperimentCatalog.find_or_create({ "basket_text" => ["purchase", "refund"] }, "Basket", "Cart")
         | 
| 17 17 | 
             
              }
         | 
| 18 18 |  | 
| 19 19 | 
             
              let(:goal1) { "purchase" }
         | 
| @@ -24,48 +24,48 @@ describe Split::Alternative do | |
| 24 24 | 
             
              end
         | 
| 25 25 |  | 
| 26 26 | 
             
              it "should have and only return the name" do
         | 
| 27 | 
            -
                expect(alternative.name).to eq( | 
| 27 | 
            +
                expect(alternative.name).to eq("Basket")
         | 
| 28 28 | 
             
              end
         | 
| 29 29 |  | 
| 30 | 
            -
              describe  | 
| 30 | 
            +
              describe "weights" do
         | 
| 31 31 | 
             
                it "should set the weights" do
         | 
| 32 | 
            -
                  experiment = Split::Experiment.new( | 
| 32 | 
            +
                  experiment = Split::Experiment.new("basket_text", alternatives: [{ "Basket" => 0.6 }, { "Cart" => 0.4 }])
         | 
| 33 33 | 
             
                  first = experiment.alternatives[0]
         | 
| 34 | 
            -
                  expect(first.name).to eq( | 
| 34 | 
            +
                  expect(first.name).to eq("Basket")
         | 
| 35 35 | 
             
                  expect(first.weight).to eq(0.6)
         | 
| 36 36 |  | 
| 37 37 | 
             
                  second = experiment.alternatives[1]
         | 
| 38 | 
            -
                  expect(second.name).to eq( | 
| 38 | 
            +
                  expect(second.name).to eq("Cart")
         | 
| 39 39 | 
             
                  expect(second.weight).to eq(0.4)
         | 
| 40 40 | 
             
                end
         | 
| 41 41 |  | 
| 42 42 | 
             
                it "accepts probability on alternatives" do
         | 
| 43 43 | 
             
                  Split.configuration.experiments = {
         | 
| 44 | 
            -
                    : | 
| 45 | 
            -
                      : | 
| 46 | 
            -
                        { : | 
| 47 | 
            -
                        { : | 
| 48 | 
            -
                        { : | 
| 44 | 
            +
                    my_experiment: {
         | 
| 45 | 
            +
                      alternatives: [
         | 
| 46 | 
            +
                        { name: "control_opt", percent: 67 },
         | 
| 47 | 
            +
                        { name: "second_opt", percent: 10 },
         | 
| 48 | 
            +
                        { name: "third_opt", percent: 23 },
         | 
| 49 49 | 
             
                      ]
         | 
| 50 50 | 
             
                    }
         | 
| 51 51 | 
             
                  }
         | 
| 52 52 | 
             
                  experiment = Split::Experiment.new(:my_experiment)
         | 
| 53 53 | 
             
                  first = experiment.alternatives[0]
         | 
| 54 | 
            -
                  expect(first.name).to eq( | 
| 54 | 
            +
                  expect(first.name).to eq("control_opt")
         | 
| 55 55 | 
             
                  expect(first.weight).to eq(0.67)
         | 
| 56 56 |  | 
| 57 57 | 
             
                  second = experiment.alternatives[1]
         | 
| 58 | 
            -
                  expect(second.name).to eq( | 
| 58 | 
            +
                  expect(second.name).to eq("second_opt")
         | 
| 59 59 | 
             
                  expect(second.weight).to eq(0.1)
         | 
| 60 60 | 
             
                end
         | 
| 61 61 |  | 
| 62 62 | 
             
                it "accepts probability on some alternatives" do
         | 
| 63 63 | 
             
                  Split.configuration.experiments = {
         | 
| 64 | 
            -
                    : | 
| 65 | 
            -
                      : | 
| 66 | 
            -
                        { : | 
| 64 | 
            +
                    my_experiment: {
         | 
| 65 | 
            +
                      alternatives: [
         | 
| 66 | 
            +
                        { name: "control_opt", percent: 34 },
         | 
| 67 67 | 
             
                        "second_opt",
         | 
| 68 | 
            -
                        { : | 
| 68 | 
            +
                        { name: "third_opt", percent: 23 },
         | 
| 69 69 | 
             
                        "fourth_opt",
         | 
| 70 70 | 
             
                      ],
         | 
| 71 71 | 
             
                    }
         | 
| @@ -87,11 +87,11 @@ describe Split::Alternative do | |
| 87 87 | 
             
                #
         | 
| 88 88 | 
             
                it "allows name param without probability" do
         | 
| 89 89 | 
             
                  Split.configuration.experiments = {
         | 
| 90 | 
            -
                    : | 
| 91 | 
            -
                      : | 
| 92 | 
            -
                        { : | 
| 90 | 
            +
                    my_experiment: {
         | 
| 91 | 
            +
                      alternatives: [
         | 
| 92 | 
            +
                        { name: "control_opt" },
         | 
| 93 93 | 
             
                        "second_opt",
         | 
| 94 | 
            -
                        { : | 
| 94 | 
            +
                        { name: "third_opt", percent: 64 },
         | 
| 95 95 | 
             
                      ],
         | 
| 96 96 | 
             
                    }
         | 
| 97 97 | 
             
                  }
         | 
| @@ -126,7 +126,7 @@ describe Split::Alternative do | |
| 126 126 |  | 
| 127 127 | 
             
              it "should save to redis" do
         | 
| 128 128 | 
             
                alternative.save
         | 
| 129 | 
            -
                expect(Split.redis.exists?( | 
| 129 | 
            +
                expect(Split.redis.exists?("basket_text:Basket")).to be true
         | 
| 130 130 | 
             
              end
         | 
| 131 131 |  | 
| 132 132 | 
             
              it "should increment participation count" do
         | 
| @@ -166,7 +166,7 @@ describe Split::Alternative do | |
| 166 166 | 
             
                expect(alternative2.control?).to be_falsey
         | 
| 167 167 | 
             
              end
         | 
| 168 168 |  | 
| 169 | 
            -
              describe  | 
| 169 | 
            +
              describe "unfinished_count" do
         | 
| 170 170 | 
             
                it "should be difference between participant and completed counts" do
         | 
| 171 171 | 
             
                  alternative.increment_participation
         | 
| 172 172 | 
             
                  expect(alternative.unfinished_count).to eq(alternative.participant_count)
         | 
| @@ -182,7 +182,7 @@ describe Split::Alternative do | |
| 182 182 | 
             
                end
         | 
| 183 183 | 
             
              end
         | 
| 184 184 |  | 
| 185 | 
            -
              describe  | 
| 185 | 
            +
              describe "conversion rate" do
         | 
| 186 186 | 
             
                it "should be 0 if there are no conversions" do
         | 
| 187 187 | 
             
                  expect(alternative.completed_count).to eq(0)
         | 
| 188 188 | 
             
                  expect(alternative.conversion_rate).to eq(0)
         | 
| @@ -225,8 +225,7 @@ describe Split::Alternative do | |
| 225 225 | 
             
                end
         | 
| 226 226 | 
             
              end
         | 
| 227 227 |  | 
| 228 | 
            -
              describe  | 
| 229 | 
            -
             | 
| 228 | 
            +
              describe "z score" do
         | 
| 230 229 | 
             
                it "should return an error string when the control has 0 people" do
         | 
| 231 230 | 
             
                  expect(alternative2.z_score).to eq("Needs 30+ participants.")
         | 
| 232 231 | 
             
                  expect(alternative2.z_score(goal1)).to eq("Needs 30+ participants.")
         | 
| @@ -269,9 +268,9 @@ describe Split::Alternative do | |
| 269 268 |  | 
| 270 269 | 
             
                it "should be N/A for the control" do
         | 
| 271 270 | 
             
                  control = experiment.control
         | 
| 272 | 
            -
                  expect(control.z_score).to eq( | 
| 273 | 
            -
                  expect(control.z_score(goal1)).to eq( | 
| 274 | 
            -
                  expect(control.z_score(goal2)).to eq( | 
| 271 | 
            +
                  expect(control.z_score).to eq("N/A")
         | 
| 272 | 
            +
                  expect(control.z_score(goal1)).to eq("N/A")
         | 
| 273 | 
            +
                  expect(control.z_score(goal2)).to eq("N/A")
         | 
| 275 274 | 
             
                end
         | 
| 276 275 |  | 
| 277 276 | 
             
                it "should not blow up for Conversion Rates > 1" do
         | 
| @@ -289,8 +288,8 @@ describe Split::Alternative do | |
| 289 288 |  | 
| 290 289 | 
             
              describe "extra_info" do
         | 
| 291 290 | 
             
                it "reads saved value of recorded_info in redis" do
         | 
| 292 | 
            -
                  saved_recorded_info = {"key_1" => 1, "key_2" => "2"}
         | 
| 293 | 
            -
                  Split.redis.hset "#{alternative.experiment_name}:#{alternative.name}",  | 
| 291 | 
            +
                  saved_recorded_info = { "key_1" => 1, "key_2" => "2" }
         | 
| 292 | 
            +
                  Split.redis.hset "#{alternative.experiment_name}:#{alternative.name}", "recorded_info", saved_recorded_info.to_json
         | 
| 294 293 | 
             
                  extra_info = alternative.extra_info
         | 
| 295 294 |  | 
| 296 295 | 
             
                  expect(extra_info).to eql(saved_recorded_info)
         | 
    
        data/spec/cache_spec.rb
    CHANGED
    
    | @@ -1,19 +1,18 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 | 
            -
            require 'spec_helper'
         | 
| 3 2 |  | 
| 4 | 
            -
             | 
| 3 | 
            +
            require "spec_helper"
         | 
| 5 4 |  | 
| 5 | 
            +
            describe Split::Cache do
         | 
| 6 6 | 
             
              let(:namespace) { :test_namespace }
         | 
| 7 7 | 
             
              let(:key) { :test_key }
         | 
| 8 8 | 
             
              let(:now) { 1606189017 }
         | 
| 9 9 |  | 
| 10 10 | 
             
              before { allow(Time).to receive(:now).and_return(now) }
         | 
| 11 11 |  | 
| 12 | 
            -
              describe  | 
| 13 | 
            -
             | 
| 12 | 
            +
              describe "clear" do
         | 
| 14 13 | 
             
                before { Split.configuration.cache = true }
         | 
| 15 14 |  | 
| 16 | 
            -
                it  | 
| 15 | 
            +
                it "clears the cache" do
         | 
| 17 16 | 
             
                  expect(Time).to receive(:now).and_return(now).exactly(2).times
         | 
| 18 17 | 
             
                  Split::Cache.fetch(namespace, key) { Time.now }
         | 
| 19 18 | 
             
                  Split::Cache.clear
         | 
| @@ -21,10 +20,10 @@ describe Split::Cache do | |
| 21 20 | 
             
                end
         | 
| 22 21 | 
             
              end
         | 
| 23 22 |  | 
| 24 | 
            -
              describe  | 
| 23 | 
            +
              describe "clear_key" do
         | 
| 25 24 | 
             
                before { Split.configuration.cache = true }
         | 
| 26 25 |  | 
| 27 | 
            -
                it  | 
| 26 | 
            +
                it "clears the cache" do
         | 
| 28 27 | 
             
                  expect(Time).to receive(:now).and_return(now).exactly(3).times
         | 
| 29 28 | 
             
                  Split::Cache.fetch(namespace, :key1) { Time.now }
         | 
| 30 29 | 
             
                  Split::Cache.fetch(namespace, :key2) { Time.now }
         | 
| @@ -35,40 +34,37 @@ describe Split::Cache do | |
| 35 34 | 
             
                end
         | 
| 36 35 | 
             
              end
         | 
| 37 36 |  | 
| 38 | 
            -
              describe  | 
| 39 | 
            -
             | 
| 37 | 
            +
              describe "fetch" do
         | 
| 40 38 | 
             
                subject { Split::Cache.fetch(namespace, key) { Time.now } }
         | 
| 41 39 |  | 
| 42 | 
            -
                context  | 
| 43 | 
            -
             | 
| 40 | 
            +
                context "when cache disabled" do
         | 
| 44 41 | 
             
                  before { Split.configuration.cache = false }
         | 
| 45 42 |  | 
| 46 | 
            -
                  it  | 
| 43 | 
            +
                  it "returns the yield" do
         | 
| 47 44 | 
             
                    expect(subject).to eql(now)
         | 
| 48 45 | 
             
                  end
         | 
| 49 46 |  | 
| 50 | 
            -
                  it  | 
| 47 | 
            +
                  it "yields every time" do
         | 
| 51 48 | 
             
                    expect(Time).to receive(:now).and_return(now).exactly(2).times
         | 
| 52 49 | 
             
                    Split::Cache.fetch(namespace, key) { Time.now }
         | 
| 53 50 | 
             
                    Split::Cache.fetch(namespace, key) { Time.now }
         | 
| 54 51 | 
             
                  end
         | 
| 55 52 | 
             
                end
         | 
| 56 53 |  | 
| 57 | 
            -
                context  | 
| 58 | 
            -
             | 
| 54 | 
            +
                context "when cache enabled" do
         | 
| 59 55 | 
             
                  before { Split.configuration.cache = true }
         | 
| 60 56 |  | 
| 61 | 
            -
                  it  | 
| 57 | 
            +
                  it "returns the yield" do
         | 
| 62 58 | 
             
                    expect(subject).to eql(now)
         | 
| 63 59 | 
             
                  end
         | 
| 64 60 |  | 
| 65 | 
            -
                  it  | 
| 61 | 
            +
                  it "yields once" do
         | 
| 66 62 | 
             
                    expect(Time).to receive(:now).and_return(now).once
         | 
| 67 63 | 
             
                    Split::Cache.fetch(namespace, key) { Time.now }
         | 
| 68 64 | 
             
                    Split::Cache.fetch(namespace, key) { Time.now }
         | 
| 69 65 | 
             
                  end
         | 
| 70 66 |  | 
| 71 | 
            -
                  it  | 
| 67 | 
            +
                  it "honors namespace" do
         | 
| 72 68 | 
             
                    expect(Split::Cache.fetch(:a, key) { :a }).to eql(:a)
         | 
| 73 69 | 
             
                    expect(Split::Cache.fetch(:b, key) { :b }).to eql(:b)
         | 
| 74 70 |  | 
| @@ -76,7 +72,7 @@ describe Split::Cache do | |
| 76 72 | 
             
                    expect(Split::Cache.fetch(:b, key) { :b }).to eql(:b)
         | 
| 77 73 | 
             
                  end
         | 
| 78 74 |  | 
| 79 | 
            -
                  it  | 
| 75 | 
            +
                  it "honors key" do
         | 
| 80 76 | 
             
                    expect(Split::Cache.fetch(namespace, :a) { :a }).to eql(:a)
         | 
| 81 77 | 
             
                    expect(Split::Cache.fetch(namespace, :b) { :b }).to eql(:b)
         | 
| 82 78 |  | 
| @@ -1,57 +1,58 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 | 
            -
             | 
| 3 | 
            -
            require  | 
| 2 | 
            +
             | 
| 3 | 
            +
            require "spec_helper"
         | 
| 4 | 
            +
            require "split/combined_experiments_helper"
         | 
| 4 5 |  | 
| 5 6 | 
             
            describe Split::CombinedExperimentsHelper do
         | 
| 6 7 | 
             
              include Split::CombinedExperimentsHelper
         | 
| 7 8 |  | 
| 8 | 
            -
              describe  | 
| 9 | 
            +
              describe "ab_combined_test" do
         | 
| 9 10 | 
             
                let!(:config_enabled) { true }
         | 
| 10 | 
            -
                let!(:combined_experiments) { [:exp_1_click, :exp_1_scroll ]}
         | 
| 11 | 
            +
                let!(:combined_experiments) { [:exp_1_click, :exp_1_scroll ] }
         | 
| 11 12 | 
             
                let!(:allow_multiple_experiments) { true }
         | 
| 12 13 |  | 
| 13 14 | 
             
                before do
         | 
| 14 15 | 
             
                  Split.configuration.experiments = {
         | 
| 15 | 
            -
                    : | 
| 16 | 
            -
                      : | 
| 17 | 
            -
                      : | 
| 18 | 
            -
                      : | 
| 16 | 
            +
                    combined_exp_1: {
         | 
| 17 | 
            +
                      alternatives: [ { "control"=> 0.5 }, { "test-alt"=> 0.5 } ],
         | 
| 18 | 
            +
                      metric: :my_metric,
         | 
| 19 | 
            +
                      combined_experiments: combined_experiments
         | 
| 19 20 | 
             
                    }
         | 
| 20 21 | 
             
                  }
         | 
| 21 22 | 
             
                  Split.configuration.enabled = config_enabled
         | 
| 22 23 | 
             
                  Split.configuration.allow_multiple_experiments = allow_multiple_experiments
         | 
| 23 24 | 
             
                end
         | 
| 24 25 |  | 
| 25 | 
            -
                context  | 
| 26 | 
            +
                context "without config enabled" do
         | 
| 26 27 | 
             
                  let!(:config_enabled) { false }
         | 
| 27 28 |  | 
| 28 29 | 
             
                  it "raises an error" do
         | 
| 29 | 
            -
                    expect | 
| 30 | 
            +
                    expect { ab_combined_test :combined_exp_1 }.to raise_error(Split::InvalidExperimentsFormatError)
         | 
| 30 31 | 
             
                  end
         | 
| 31 32 | 
             
                end
         | 
| 32 33 |  | 
| 33 | 
            -
                context  | 
| 34 | 
            +
                context "multiple experiments disabled" do
         | 
| 34 35 | 
             
                  let!(:allow_multiple_experiments) { false }
         | 
| 35 36 |  | 
| 36 37 | 
             
                  it "raises an error if multiple experiments is disabled" do
         | 
| 37 | 
            -
                    expect | 
| 38 | 
            +
                    expect { ab_combined_test :combined_exp_1 }.to raise_error(Split::InvalidExperimentsFormatError)
         | 
| 38 39 | 
             
                  end
         | 
| 39 40 | 
             
                end
         | 
| 40 41 |  | 
| 41 | 
            -
                context  | 
| 42 | 
            +
                context "without combined experiments" do
         | 
| 42 43 | 
             
                  let!(:combined_experiments) { nil }
         | 
| 43 44 |  | 
| 44 45 | 
             
                  it "raises an error" do
         | 
| 45 | 
            -
                    expect | 
| 46 | 
            +
                    expect { ab_combined_test :combined_exp_1 }.to raise_error(Split::InvalidExperimentsFormatError)
         | 
| 46 47 | 
             
                  end
         | 
| 47 48 | 
             
                end
         | 
| 48 49 |  | 
| 49 50 | 
             
                it "uses same alternative for all sub experiments and returns the alternative" do
         | 
| 50 51 | 
             
                  allow(self).to receive(:get_alternative) { "test-alt" }
         | 
| 51 | 
            -
                  expect(self).to receive(:ab_test).with(:exp_1_click, {"control"=>0.5}, {"test-alt"=>0.5}) { "test-alt" }
         | 
| 52 | 
            -
                  expect(self).to receive(:ab_test).with(:exp_1_scroll, [{"control" => 0, "test-alt" => 1}])
         | 
| 52 | 
            +
                  expect(self).to receive(:ab_test).with(:exp_1_click, { "control"=>0.5 }, { "test-alt"=>0.5 }) { "test-alt" }
         | 
| 53 | 
            +
                  expect(self).to receive(:ab_test).with(:exp_1_scroll, [{ "control" => 0, "test-alt" => 1 }])
         | 
| 53 54 |  | 
| 54 | 
            -
                  expect(ab_combined_test( | 
| 55 | 
            +
                  expect(ab_combined_test("combined_exp_1")).to eq("test-alt")
         | 
| 55 56 | 
             
                end
         | 
| 56 57 | 
             
              end
         | 
| 57 58 | 
             
            end
         | 
    
        data/spec/configuration_spec.rb
    CHANGED
    
    | @@ -1,8 +1,8 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 | 
            -
            require 'spec_helper'
         | 
| 3 2 |  | 
| 4 | 
            -
             | 
| 3 | 
            +
            require "spec_helper"
         | 
| 5 4 |  | 
| 5 | 
            +
            describe Split::Configuration do
         | 
| 6 6 | 
             
              before(:each) { @config = Split::Configuration.new }
         | 
| 7 7 |  | 
| 8 8 | 
             
              it "should provide a default value for ignore_ip_addresses" do
         | 
| @@ -58,17 +58,15 @@ describe Split::Configuration do | |
| 58 58 | 
             
              end
         | 
| 59 59 |  | 
| 60 60 | 
             
              it "should load a metric" do
         | 
| 61 | 
            -
                @config.experiments = {: | 
| 62 | 
            -
                    {:alternatives=>["control_opt", "other_opt"], :metric=>:my_metric}}
         | 
| 61 | 
            +
                @config.experiments = { my_experiment: { alternatives: ["control_opt", "other_opt"], metric: :my_metric } }
         | 
| 63 62 |  | 
| 64 63 | 
             
                expect(@config.metrics).not_to be_nil
         | 
| 65 64 | 
             
                expect(@config.metrics.keys).to eq([:my_metric])
         | 
| 66 65 | 
             
              end
         | 
| 67 66 |  | 
| 68 67 | 
             
              it "should allow loading of experiment using experment_for" do
         | 
| 69 | 
            -
                @config.experiments = {: | 
| 70 | 
            -
             | 
| 71 | 
            -
                expect(@config.experiment_for(:my_experiment)).to eq({:alternatives=>["control_opt", ["other_opt"]]})
         | 
| 68 | 
            +
                @config.experiments = { my_experiment: { alternatives: ["control_opt", "other_opt"], metric: :my_metric } }
         | 
| 69 | 
            +
                expect(@config.experiment_for(:my_experiment)).to eq({ alternatives: ["control_opt", ["other_opt"]] })
         | 
| 72 70 | 
             
              end
         | 
| 73 71 |  | 
| 74 72 | 
             
              context "when experiments are defined via YAML" do
         | 
| @@ -82,12 +80,12 @@ describe Split::Configuration do | |
| 82 80 | 
             
                            - Alt One
         | 
| 83 81 | 
             
                            - Alt Two
         | 
| 84 82 | 
             
                          resettable: false
         | 
| 85 | 
            -
             | 
| 83 | 
            +
                      eos
         | 
| 86 84 | 
             
                      @config.experiments = YAML.load(experiments_yaml)
         | 
| 87 85 | 
             
                    end
         | 
| 88 86 |  | 
| 89 | 
            -
                    it  | 
| 90 | 
            -
                      expect(@config.normalized_experiments).to eq({: | 
| 87 | 
            +
                    it "should normalize experiments" do
         | 
| 88 | 
            +
                      expect(@config.normalized_experiments).to eq({ my_experiment: { resettable: false, alternatives: ["Control Opt", ["Alt One", "Alt Two"]] } })
         | 
| 91 89 | 
             
                    end
         | 
| 92 90 | 
             
                  end
         | 
| 93 91 |  | 
| @@ -110,14 +108,14 @@ describe Split::Configuration do | |
| 110 108 | 
             
                            Alt Two:
         | 
| 111 109 | 
             
                              text: 'Alternative Two'
         | 
| 112 110 | 
             
                          resettable: false
         | 
| 113 | 
            -
             | 
| 111 | 
            +
                      eos
         | 
| 114 112 | 
             
                      @config.experiments = YAML.load(experiments_yaml)
         | 
| 115 113 | 
             
                    end
         | 
| 116 114 |  | 
| 117 | 
            -
                    it  | 
| 115 | 
            +
                    it "should have metadata on the experiment" do
         | 
| 118 116 | 
             
                      meta = @config.normalized_experiments[:my_experiment][:metadata]
         | 
| 119 117 | 
             
                      expect(meta).to_not be nil
         | 
| 120 | 
            -
                      expect(meta[ | 
| 118 | 
            +
                      expect(meta["Control Opt"]["text"]).to eq("Control Option")
         | 
| 121 119 | 
             
                    end
         | 
| 122 120 | 
             
                  end
         | 
| 123 121 |  | 
| @@ -138,25 +136,23 @@ describe Split::Configuration do | |
| 138 136 | 
             
                          alternatives:
         | 
| 139 137 | 
             
                            - a
         | 
| 140 138 | 
             
                            - b
         | 
| 141 | 
            -
             | 
| 139 | 
            +
                      eos
         | 
| 142 140 | 
             
                      @config.experiments = YAML.load(experiments_yaml)
         | 
| 143 141 | 
             
                    end
         | 
| 144 142 |  | 
| 145 143 | 
             
                    it "should normalize experiments" do
         | 
| 146 | 
            -
                      expect(@config.normalized_experiments).to eq({: | 
| 147 | 
            -
                        [{"Alt One"=>0.1}, {"Alt Two"=>0.23}]]}, : | 
| 144 | 
            +
                      expect(@config.normalized_experiments).to eq({ my_experiment: { resettable: false, alternatives: [{ "Control Opt"=>0.67 },
         | 
| 145 | 
            +
                        [{ "Alt One"=>0.1 }, { "Alt Two"=>0.23 }]] }, another_experiment: { alternatives: ["a", ["b"]] } })
         | 
| 148 146 | 
             
                    end
         | 
| 149 147 |  | 
| 150 148 | 
             
                    it "should recognize metrics" do
         | 
| 151 149 | 
             
                      expect(@config.metrics).not_to be_nil
         | 
| 152 150 | 
             
                      expect(@config.metrics.keys).to eq([:my_metric])
         | 
| 153 151 | 
             
                    end
         | 
| 154 | 
            -
             | 
| 155 152 | 
             
                  end
         | 
| 156 153 | 
             
                end
         | 
| 157 154 |  | 
| 158 155 | 
             
                context "as symbols" do
         | 
| 159 | 
            -
             | 
| 160 156 | 
             
                  context "with valid YAML" do
         | 
| 161 157 | 
             
                    before do
         | 
| 162 158 | 
             
                      experiments_yaml = <<-eos
         | 
| @@ -166,21 +162,20 @@ describe Split::Configuration do | |
| 166 162 | 
             
                            - Alt One
         | 
| 167 163 | 
             
                            - Alt Two
         | 
| 168 164 | 
             
                          :resettable: false
         | 
| 169 | 
            -
             | 
| 165 | 
            +
                      eos
         | 
| 170 166 | 
             
                      @config.experiments = YAML.load(experiments_yaml)
         | 
| 171 167 | 
             
                    end
         | 
| 172 168 |  | 
| 173 169 | 
             
                    it "should normalize experiments" do
         | 
| 174 | 
            -
                      expect(@config.normalized_experiments).to eq({: | 
| 170 | 
            +
                      expect(@config.normalized_experiments).to eq({ my_experiment: { resettable: false, alternatives: ["Control Opt", ["Alt One", "Alt Two"]] } })
         | 
| 175 171 | 
             
                    end
         | 
| 176 172 | 
             
                  end
         | 
| 177 173 |  | 
| 178 174 | 
             
                  context "with invalid YAML" do
         | 
| 179 | 
            -
             | 
| 180 175 | 
             
                    let(:yaml) { YAML.load(input) }
         | 
| 181 176 |  | 
| 182 177 | 
             
                    context "with an empty string" do
         | 
| 183 | 
            -
                      let(:input) {  | 
| 178 | 
            +
                      let(:input) { "" }
         | 
| 184 179 |  | 
| 185 180 | 
             
                      it "should raise an error" do
         | 
| 186 181 | 
             
                        expect { @config.experiments = yaml }.to raise_error(Split::InvalidExperimentsFormatError)
         | 
| @@ -188,7 +183,7 @@ describe Split::Configuration do | |
| 188 183 | 
             
                    end
         | 
| 189 184 |  | 
| 190 185 | 
             
                    context "with just the YAML header" do
         | 
| 191 | 
            -
                      let(:input) {  | 
| 186 | 
            +
                      let(:input) { "---" }
         | 
| 192 187 |  | 
| 193 188 | 
             
                      it "should raise an error" do
         | 
| 194 189 | 
             
                        expect { @config.experiments = yaml }.to raise_error(Split::InvalidExperimentsFormatError)
         | 
| @@ -200,24 +195,24 @@ describe Split::Configuration do | |
| 200 195 |  | 
| 201 196 | 
             
              it "should normalize experiments" do
         | 
| 202 197 | 
             
                @config.experiments = {
         | 
| 203 | 
            -
                  : | 
| 204 | 
            -
                    : | 
| 205 | 
            -
                      { : | 
| 206 | 
            -
                      { : | 
| 207 | 
            -
                      { : | 
| 198 | 
            +
                  my_experiment: {
         | 
| 199 | 
            +
                    alternatives: [
         | 
| 200 | 
            +
                      { name: "control_opt", percent: 67 },
         | 
| 201 | 
            +
                      { name: "second_opt", percent: 10 },
         | 
| 202 | 
            +
                      { name: "third_opt", percent: 23 },
         | 
| 208 203 | 
             
                    ],
         | 
| 209 204 | 
             
                  }
         | 
| 210 205 | 
             
                }
         | 
| 211 206 |  | 
| 212 | 
            -
                expect(@config.normalized_experiments).to eq({: | 
| 207 | 
            +
                expect(@config.normalized_experiments).to eq({ my_experiment: { alternatives: [{ "control_opt"=>0.67 }, [{ "second_opt"=>0.1 }, { "third_opt"=>0.23 }]] } })
         | 
| 213 208 | 
             
              end
         | 
| 214 209 |  | 
| 215 210 | 
             
              context "redis configuration" do
         | 
| 216 211 | 
             
                it "should default to local redis server" do
         | 
| 217 | 
            -
                  old_redis_url = ENV[ | 
| 218 | 
            -
                  ENV.delete( | 
| 212 | 
            +
                  old_redis_url = ENV["REDIS_URL"]
         | 
| 213 | 
            +
                  ENV.delete("REDIS_URL")
         | 
| 219 214 | 
             
                  expect(Split::Configuration.new.redis).to eq("redis://localhost:6379")
         | 
| 220 | 
            -
                  ENV[ | 
| 215 | 
            +
                  ENV["REDIS_URL"] = old_redis_url
         | 
| 221 216 | 
             
                end
         | 
| 222 217 |  | 
| 223 218 | 
             
                it "should allow for redis url to be configured" do
         | 
| @@ -227,10 +222,10 @@ describe Split::Configuration do | |
| 227 222 |  | 
| 228 223 | 
             
                context "provided REDIS_URL environment variable" do
         | 
| 229 224 | 
             
                  it "should use the ENV variable" do
         | 
| 230 | 
            -
                    old_redis_url = ENV[ | 
| 231 | 
            -
                    ENV[ | 
| 225 | 
            +
                    old_redis_url = ENV["REDIS_URL"]
         | 
| 226 | 
            +
                    ENV["REDIS_URL"] = "env_redis_url"
         | 
| 232 227 | 
             
                    expect(Split::Configuration.new.redis).to eq("env_redis_url")
         | 
| 233 | 
            -
                    ENV[ | 
| 228 | 
            +
                    ENV["REDIS_URL"] = old_redis_url
         | 
| 234 229 | 
             
                  end
         | 
| 235 230 | 
             
                end
         | 
| 236 231 | 
             
              end
         | 
| @@ -252,9 +247,8 @@ describe Split::Configuration do | |
| 252 247 | 
             
                end
         | 
| 253 248 |  | 
| 254 249 | 
             
                it "should allow the persistence cookie domain to be configured" do
         | 
| 255 | 
            -
                  @config.persistence_cookie_domain =  | 
| 256 | 
            -
                  expect(@config.persistence_cookie_domain).to eq( | 
| 250 | 
            +
                  @config.persistence_cookie_domain = ".acme.com"
         | 
| 251 | 
            +
                  expect(@config.persistence_cookie_domain).to eq(".acme.com")
         | 
| 257 252 | 
             
                end
         | 
| 258 253 | 
             
              end
         | 
| 259 | 
            -
             | 
| 260 254 | 
             
            end
         |