Split Ruby Documentation

repository·main·Indexed 25 days ago

https://github.com/splitrb/split

A Rack-based A/B testing framework for Rails, Sinatra, and other Rack applications. Split uses Redis as a backend datastore to manage experiments, track conversions via ab_finished, and determine winning alternatives using beta distribution simulations. It includes a Sinatra-based dashboard for monitoring, support for custom persistence adapters (Cookies, Redis, Dual), and RSpec helpers for testing.

Tokens
9.9K
Snippets
25
Records
69
Agent score
82%

What's inside Split

  1. Use the Rspec Helper for testing

    main

    To facilitate testing in RSpec, create a helper module to mock ab_test calls.

    1. Create spec/support/split_helper.rb with the following content:
    module SplitHelper
      def use_ab_test(alternatives_by_experiment)
        allow_any_instance_of(Split::Helper).to receive(:ab_test) do |_receiver, experiment, &block|
          variant = alternatives_by_experiment.fetch(experiment) { |key| raise "Unknown experiment '#{key}'" }
          block.call(variant) unless block.nil?
          variant
        end
      end
    end
    
    RSpec.configure do |config|
      config.include SplitHelper
    end
    1. Use it in your specs:
    it "registers using experimental signup" do
      use_ab_test(signup_form: "single_page")
      post "/signups"
      # ...
    end
    # Create a file with these contents at 'spec/support/split_helper.rb'
    # and ensure it is `require`d in your rails_helper.rb or spec_helper.rb
    module SplitHelper
    
      # Force a specific experiment alternative to always be returned:
      #   use_ab_test(signup_form: "single_page")
      #
      # Force alternatives for multiple experiments:
      #   use_ab_test(signup_form: "single_page", pricing: "show_enterprise_prices")
      #
      def use_ab_test(alternatives_by_experiment)
        allow_any_instance_of(Split::Helper).to receive(:ab_test) do |_receiver, experiment, &block|
          variant = alternatives_by_experiment.fetch(experiment) { |key| raise "Unknown experiment '#{key}'" }
          block.call(variant) unless block.nil?
          variant
        end
      end
    end
    
    # Make the `use_ab_test` method available to all specs:
    RSpec.configure do |config|
      config.include SplitHelper
    end
  2. Mount the Split Web Dashboard

    main

    Split includes a Sinatra-based dashboard to monitor experiments. The mounting method depends on your Rails version.

    Rails 2

    Mount using Rack::URLMap in your config.ru:

    Rails 3 or higher

    1. Add to your Gemfile: gem 'split', require: 'split/dashboard'
    2. Add to config/routes.rb: mount Split::Dashboard, at: 'split'
    # Rails 2 (config.ru)
    require 'split/dashboard'
    
    run Rack::URLMap.new \
      "/"       => Your::App.new,
      "/split" => Split::Dashboard.new
    # Rails 3+ (config/routes.rb)
    mount Split::Dashboard, at: 'split'
  3. Run Combined Experiments

    main

    To test multiple effects (e.g., button color affecting both signup AND login) simultaneously, use combined_experiments.

    1. Configure: Add combined_experiments to the main experiment config.
    2. Start: Use ab_combined_test(:main_experiment).
    3. Finish: Finish each sub-experiment individually using ab_finished(:sub_experiment).

    Requirements:

    • allow_multiple_experiments must be true in config.
    • In Sinatra, include helpers Split::CombinedExperimentsHelper.
    # Configuration
    Split.configuration.experiments = {
      :button_color_experiment => {
        :alternatives => ["blue", "green"],
        :combined_experiments => ["button_color_on_signup", "button_color_on_login"]
      }
    }
    
    # Usage
    ab_combined_test(:button_color_experiment)
    
    # Finishing
    ab_finished(:button_color_on_login)
    ab_finished(:button_color_on_signup)
  4. Track Metrics and Goals

    main

    Metrics

    Metrics allow you to group multiple experiments under a single name.

    1. Create experiments using Split::ExperimentCatalog.find_or_create.
    2. Create a metric: Split::Metric.new(name: :my_metric, experiments: [exp1, exp2]).save.
    3. Complete the metric: ab_finished(:my_metric).

    Goals

    Experiments can have multiple goals (e.g., ['purchase', 'refund']).

    1. Define goals in config or inline: ab_test({link_color: ['purchase', 'refund']}, 'red', 'blue').
    2. Complete a goal: ab_finished(link_color: 'purchase').

    Warning: An experiment can only complete one goal. Once one goal is finished, the experiment is considered complete and subsequent goal completions will not register (if reset: false).

  5. Override A/B test alternatives via URL

    main

    For development or testing, you can force an experiment to return a specific alternative using a URL parameter.

    Format: ?ab_test[experiment_name]=alternative_name

    Example: http://myawesomesite.com?ab_test[button_color]=red

    Note: This override is not stored in the session and does not count towards results unless the store_override configuration option is enabled.

    To disable all tests globally via URL, use the SPLIT_DISABLE parameter: http://myawesomesite.com?SPLIT_DISABLE=true.

  6. Configure Redis for Split

    main

    Split requires a running Redis daemon. If you are on OS X, you can install and run Redis using Homebrew:

    brew install redis
    redis-server /usr/local/etc/redis.conf

    By default, the Redis daemon will run on port 6379.

  7. Protect the Split Dashboard with Authentication

    main

    You can secure the dashboard using Rack::Auth::Basic.

    Using ActiveSupport (Rails apps)

    Use ActiveSupport::SecurityUtils.secure_compare to protect against timing attacks. Use the & operator (not &&) to prevent short-circuiting.

    Without ActiveSupport

    Use Rack::Utils.secure_compare.

    Using Devise/Warden

    To use existing authentication systems like Devise, use a route constraint in config/routes.rb to check request.env['warden'].authenticated?.

    # Rails apps with ActiveSupport
    Split::Dashboard.use Rack::Auth::Basic do |username, password|
      ActiveSupport::SecurityUtils.secure_compare(::Digest::SHA256.hexdigest(username), ::Digest::SHA256.hexdigest(ENV["SPLIT_USERNAME"])) &
        ActiveSupport::SecurityUtils.secure_compare(::Digest::SHA256.hexdigest(password), ::Digest::SHA256.hexdigest(ENV["SPLIT_PASSWORD"]))
    end
    
    # Using Devise/Warden in routes.rb
    match "/split" => Split::Dashboard, anchor: false, via: [:get, :post, :delete], constraints: -> (request) do
      request.env['warden'].authenticated?
      request.env['warden'].authenticate!
    end
  8. Setup Split in Sinatra

    main

    To use Split in a Sinatra application, you must enable sessions and mix in the Split::Helper methods. Add the following to your Sinatra app:

    require 'split'
    
    class MySinatraApp < Sinatra::Base
      enable :sessions
      helpers Split::Helper
    
      get '/' do
        # ...
      end
    end
  9. Namespace Redis keyspaces using redis-namespace

    main

    If you are running multiple separate instances of Split, you can prevent key overlap by namespacing your Redis keys. To do this, add the redis-namespace gem to your Gemfile and configure Split.redis with a Redis::Namespace instance in an initializer.

    1. Add to Gemfile:
    gem 'redis-namespace'
    1. Configure in an initializer:
    redis = Redis.new(url: ENV['REDIS_URL'])
    Split.redis = Redis::Namespace.new(:your_namespace, redis: redis)
    gem 'redis-namespace'
    
    redis = Redis.new(url: ENV['REDIS_URL'])
    Split.redis = Redis::Namespace.new(:your_namespace, redis: redis)