diff --git a/.rubocop.yml b/.rubocop.yml index c2cf054a..b2b652fb 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1,8 +1,7 @@ -inherit_from: .rubocop_todo.yml - AllCops: TargetRubyVersion: 2.5 DisabledByDefault: true + SuggestExtensions: false Exclude: - 'gemfiles/**/*' @@ -112,7 +111,7 @@ Layout/SpaceInsideParens: Enabled: true Style/StringLiterals: - Enabled: false + Enabled: true EnforcedStyle: double_quotes Layout/IndentationStyle: diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml deleted file mode 100644 index f473a0ec..00000000 --- a/.rubocop_todo.yml +++ /dev/null @@ -1,14 +0,0 @@ -# This configuration was generated by -# `rubocop --auto-gen-config` -# on 2020-07-05 01:43:26 UTC using RuboCop version 0.86.0. -# The point is for the user to remove these configuration records -# one by one as the offenses are removed from the code base. -# Note that changes in the inspected code, or installation of new -# versions of RuboCop, may require this file to be generated again. - -# Offense count: 258 -# Cop supports --auto-correct. -# Configuration parameters: EnforcedStyle, ConsistentQuotesInMultiline. -# SupportedStyles: single_quotes, double_quotes -Style/StringLiterals: - Enabled: false diff --git a/Gemfile b/Gemfile index d5afa8de..9fdc310a 100644 --- a/Gemfile +++ b/Gemfile @@ -4,6 +4,6 @@ source "https://rubygems.org" gemspec -gem 'rubocop', require: false +gem "rubocop", require: false gem "matrix" gem "codeclimate-test-reporter" diff --git a/Rakefile b/Rakefile index a7f213b6..4555c708 100755 --- a/Rakefile +++ b/Rakefile @@ -1,9 +1,9 @@ #!/usr/bin/env rake # frozen_string_literal: true -require 'bundler/gem_tasks' -require 'rspec/core/rake_task' +require "bundler/gem_tasks" +require "rspec/core/rake_task" -RSpec::Core::RakeTask.new('spec') +RSpec::Core::RakeTask.new("spec") task default: :spec diff --git a/lib/split.rb b/lib/split.rb index 6d1c6c05..1b24b787 100755 --- a/lib/split.rb +++ b/lib/split.rb @@ -1,30 +1,30 @@ # frozen_string_literal: true -require 'redis' +require "redis" -require 'split/algorithms' -require 'split/algorithms/block_randomization' -require 'split/algorithms/weighted_sample' -require 'split/algorithms/whiplash' -require 'split/alternative' -require 'split/cache' -require 'split/configuration' -require 'split/encapsulated_helper' -require 'split/exceptions' -require 'split/experiment' -require 'split/experiment_catalog' -require 'split/extensions/string' -require 'split/goals_collection' -require 'split/helper' -require 'split/combined_experiments_helper' -require 'split/metric' -require 'split/persistence' -require 'split/redis_interface' -require 'split/trial' -require 'split/user' -require 'split/version' -require 'split/zscore' -require 'split/engine' if defined?(Rails) +require "split/algorithms" +require "split/algorithms/block_randomization" +require "split/algorithms/weighted_sample" +require "split/algorithms/whiplash" +require "split/alternative" +require "split/cache" +require "split/configuration" +require "split/encapsulated_helper" +require "split/exceptions" +require "split/experiment" +require "split/experiment_catalog" +require "split/extensions/string" +require "split/goals_collection" +require "split/helper" +require "split/combined_experiments_helper" +require "split/metric" +require "split/persistence" +require "split/redis_interface" +require "split/trial" +require "split/user" +require "split/version" +require "split/zscore" +require "split/engine" if defined?(Rails) module Split extend self diff --git a/lib/split/algorithms.rb b/lib/split/algorithms.rb index d4317b82..14631597 100644 --- a/lib/split/algorithms.rb +++ b/lib/split/algorithms.rb @@ -9,7 +9,7 @@ end end -require 'rubystats' +require "rubystats" module Split module Algorithms diff --git a/lib/split/alternative.rb b/lib/split/alternative.rb index 554a7326..3d416188 100644 --- a/lib/split/alternative.rb +++ b/lib/split/alternative.rb @@ -38,11 +38,11 @@ def set_p_winner(prob, goal = nil) end def participant_count - Split.redis.hget(key, 'participant_count').to_i + Split.redis.hget(key, "participant_count").to_i end def participant_count=(count) - Split.redis.hset(key, 'participant_count', count.to_i) + Split.redis.hset(key, "participant_count", count.to_i) end def completed_count(goal = nil) @@ -82,7 +82,7 @@ def set_completed_count(count, goal = nil) end def increment_participation - Split.redis.hincrby key, 'participant_count', 1 + Split.redis.hincrby key, "participant_count", 1 end def increment_completion(goal = nil) @@ -112,7 +112,7 @@ def z_score(goal = nil) control = experiment.control alternative = self - return 'N/A' if control.name == alternative.name + return "N/A" if control.name == alternative.name p_a = alternative.conversion_rate(goal) p_c = control.conversion_rate(goal) @@ -121,13 +121,13 @@ def z_score(goal = nil) n_c = control.participant_count # can't calculate zscore for P(x) > 1 - return 'N/A' if p_a > 1 || p_c > 1 + return "N/A" if p_a > 1 || p_c > 1 Split::Zscore.calculate(p_a, n_a, p_c, n_c) end def extra_info - data = Split.redis.hget(key, 'recorded_info') + data = Split.redis.hget(key, "recorded_info") if data && data.length > 1 begin JSON.parse(data) @@ -149,24 +149,24 @@ def record_extra_info(k, value = 1) @recorded_info[k] = value end - Split.redis.hset key, 'recorded_info', (@recorded_info || {}).to_json + Split.redis.hset key, "recorded_info", (@recorded_info || {}).to_json end def save - Split.redis.hsetnx key, 'participant_count', 0 - Split.redis.hsetnx key, 'completed_count', 0 - Split.redis.hsetnx key, 'p_winner', p_winner - Split.redis.hsetnx key, 'recorded_info', (@recorded_info || {}).to_json + Split.redis.hsetnx key, "participant_count", 0 + Split.redis.hsetnx key, "completed_count", 0 + Split.redis.hsetnx key, "p_winner", p_winner + Split.redis.hsetnx key, "recorded_info", (@recorded_info || {}).to_json end def validate! unless String === @name || hash_with_correct_values?(@name) - raise ArgumentError, 'Alternative must be a string' + raise ArgumentError, "Alternative must be a string" end end def reset - Split.redis.hmset key, 'participant_count', 0, 'completed_count', 0, 'recorded_info', nil + Split.redis.hmset key, "participant_count", 0, "completed_count", 0, "recorded_info", nil unless goals.empty? goals.each do |g| field = "completed_count:#{g}" diff --git a/lib/split/combined_experiments_helper.rb b/lib/split/combined_experiments_helper.rb index b925901f..dd35d791 100644 --- a/lib/split/combined_experiments_helper.rb +++ b/lib/split/combined_experiments_helper.rb @@ -29,9 +29,9 @@ def ab_combined_test(metric_descriptor, control = nil, *alternatives) end def find_combined_experiment(metric_descriptor) - raise(Split::InvalidExperimentsFormatError, 'Invalid descriptor class (String or Symbol required)') unless metric_descriptor.class == String || metric_descriptor.class == Symbol - raise(Split::InvalidExperimentsFormatError, 'Enable configuration') unless Split.configuration.enabled - raise(Split::InvalidExperimentsFormatError, 'Enable `allow_multiple_experiments`') unless Split.configuration.allow_multiple_experiments + raise(Split::InvalidExperimentsFormatError, "Invalid descriptor class (String or Symbol required)") unless metric_descriptor.class == String || metric_descriptor.class == Symbol + raise(Split::InvalidExperimentsFormatError, "Enable configuration") unless Split.configuration.enabled + raise(Split::InvalidExperimentsFormatError, "Enable `allow_multiple_experiments`") unless Split.configuration.allow_multiple_experiments Split.configuration.experiments[metric_descriptor.to_sym] end end diff --git a/lib/split/configuration.rb b/lib/split/configuration.rb index 02977c3e..b43e5920 100644 --- a/lib/split/configuration.rb +++ b/lib/split/configuration.rb @@ -39,83 +39,83 @@ class Configuration def bots @bots ||= { # Indexers - 'AdsBot-Google' => 'Google Adwords', - 'Baidu' => 'Chinese search engine', - 'Baiduspider' => 'Chinese search engine', - 'bingbot' => 'Microsoft bing bot', - 'Butterfly' => 'Topsy Labs', - 'Gigabot' => 'Gigabot spider', - 'Googlebot' => 'Google spider', - 'MJ12bot' => 'Majestic-12 spider', - 'msnbot' => 'Microsoft bot', - 'rogerbot' => 'SeoMoz spider', - 'PaperLiBot' => 'PaperLi is another content curation service', - 'Slurp' => 'Yahoo spider', - 'Sogou' => 'Chinese search engine', - 'spider' => 'generic web spider', - 'UnwindFetchor' => 'Gnip crawler', - 'WordPress' => 'WordPress spider', - 'YandexAccessibilityBot' => 'Yandex accessibility spider', - 'YandexBot' => 'Yandex spider', - 'YandexMobileBot' => 'Yandex mobile spider', - 'ZIBB' => 'ZIBB spider', + "AdsBot-Google" => "Google Adwords", + "Baidu" => "Chinese search engine", + "Baiduspider" => "Chinese search engine", + "bingbot" => "Microsoft bing bot", + "Butterfly" => "Topsy Labs", + "Gigabot" => "Gigabot spider", + "Googlebot" => "Google spider", + "MJ12bot" => "Majestic-12 spider", + "msnbot" => "Microsoft bot", + "rogerbot" => "SeoMoz spider", + "PaperLiBot" => "PaperLi is another content curation service", + "Slurp" => "Yahoo spider", + "Sogou" => "Chinese search engine", + "spider" => "generic web spider", + "UnwindFetchor" => "Gnip crawler", + "WordPress" => "WordPress spider", + "YandexAccessibilityBot" => "Yandex accessibility spider", + "YandexBot" => "Yandex spider", + "YandexMobileBot" => "Yandex mobile spider", + "ZIBB" => "ZIBB spider", # HTTP libraries - 'Apache-HttpClient' => 'Java http library', - 'AppEngine-Google' => 'Google App Engine', - 'curl' => 'curl unix CLI http client', - 'ColdFusion' => 'ColdFusion http library', - 'EventMachine HttpClient' => 'Ruby http library', - 'Go http package' => 'Go http library', - 'Go-http-client' => 'Go http library', - 'Java' => 'Generic Java http library', - 'libwww-perl' => 'Perl client-server library loved by script kids', - 'lwp-trivial' => 'Another Perl library loved by script kids', - 'Python-urllib' => 'Python http library', - 'PycURL' => 'Python http library', - 'Test Certificate Info' => 'C http library?', - 'Typhoeus' => 'Ruby http library', - 'Wget' => 'wget unix CLI http client', + "Apache-HttpClient" => "Java http library", + "AppEngine-Google" => "Google App Engine", + "curl" => "curl unix CLI http client", + "ColdFusion" => "ColdFusion http library", + "EventMachine HttpClient" => "Ruby http library", + "Go http package" => "Go http library", + "Go-http-client" => "Go http library", + "Java" => "Generic Java http library", + "libwww-perl" => "Perl client-server library loved by script kids", + "lwp-trivial" => "Another Perl library loved by script kids", + "Python-urllib" => "Python http library", + "PycURL" => "Python http library", + "Test Certificate Info" => "C http library?", + "Typhoeus" => "Ruby http library", + "Wget" => "wget unix CLI http client", # URL expanders / previewers - 'awe.sm' => 'Awe.sm URL expander', - 'bitlybot' => 'bit.ly bot', - 'bot@linkfluence.net' => 'Linkfluence bot', - 'facebookexternalhit' => 'facebook bot', - 'Facebot' => 'Facebook crawler', - 'Feedfetcher-Google' => 'Google Feedfetcher', - 'https://developers.google.com/+/web/snippet' => 'Google+ Snippet Fetcher', - 'LinkedInBot' => 'LinkedIn bot', - 'LongURL' => 'URL expander service', - 'NING' => 'NING - Yet Another Twitter Swarmer', - 'Pinterestbot' => 'Pinterest Bot', - 'redditbot' => 'Reddit Bot', - 'ShortLinkTranslate' => 'Link shortener', - 'Slackbot' => 'Slackbot link expander', - 'TweetmemeBot' => 'TweetMeMe Crawler', - 'Twitterbot' => 'Twitter URL expander', - 'UnwindFetch' => 'Gnip URL expander', - 'vkShare' => 'VKontake Sharer', + "awe.sm" => "Awe.sm URL expander", + "bitlybot" => "bit.ly bot", + "bot@linkfluence.net" => "Linkfluence bot", + "facebookexternalhit" => "facebook bot", + "Facebot" => "Facebook crawler", + "Feedfetcher-Google" => "Google Feedfetcher", + "https://developers.google.com/+/web/snippet" => "Google+ Snippet Fetcher", + "LinkedInBot" => "LinkedIn bot", + "LongURL" => "URL expander service", + "NING" => "NING - Yet Another Twitter Swarmer", + "Pinterestbot" => "Pinterest Bot", + "redditbot" => "Reddit Bot", + "ShortLinkTranslate" => "Link shortener", + "Slackbot" => "Slackbot link expander", + "TweetmemeBot" => "TweetMeMe Crawler", + "Twitterbot" => "Twitter URL expander", + "UnwindFetch" => "Gnip URL expander", + "vkShare" => "VKontake Sharer", # Uptime monitoring - 'check_http' => 'Nagios monitor', - 'GoogleStackdriverMonitoring' => 'Google Cloud monitor', - 'NewRelicPinger' => 'NewRelic monitor', - 'Panopta' => 'Monitoring service', - 'Pingdom' => 'Pingdom monitoring', - 'SiteUptime' => 'Site monitoring services', - 'UptimeRobot' => 'Monitoring service', + "check_http" => "Nagios monitor", + "GoogleStackdriverMonitoring" => "Google Cloud monitor", + "NewRelicPinger" => "NewRelic monitor", + "Panopta" => "Monitoring service", + "Pingdom" => "Pingdom monitoring", + "SiteUptime" => "Site monitoring services", + "UptimeRobot" => "Monitoring service", # ??? - 'DigitalPersona Fingerprint Software' => 'HP Fingerprint scanner', - 'ShowyouBot' => 'Showyou iOS app spider', - 'ZyBorg' => 'Zyborg? Hmmm....', - 'ELB-HealthChecker' => 'ELB Health Check' + "DigitalPersona Fingerprint Software" => "HP Fingerprint scanner", + "ShowyouBot" => "Showyou iOS app spider", + "ZyBorg" => "Zyborg? Hmmm....", + "ELB-HealthChecker" => "ELB Health Check" } end def experiments=(experiments) - raise InvalidExperimentsFormatError.new('Experiments must be a Hash') unless experiments.respond_to?(:keys) + raise InvalidExperimentsFormatError.new("Experiments must be a Hash") unless experiments.respond_to?(:keys) @experiments = experiments end @@ -232,7 +232,7 @@ def initialize @include_rails_helper = true @beta_probability_simulations = 10000 @winning_alternative_recalculation_interval = 60 * 60 * 24 # 1 day - @redis = ENV.fetch(ENV.fetch('REDIS_PROVIDER', 'REDIS_URL'), 'redis://localhost:6379') + @redis = ENV.fetch(ENV.fetch("REDIS_PROVIDER", "REDIS_URL"), "redis://localhost:6379") @dashboard_pagination_default_per_page = 10 end diff --git a/lib/split/dashboard.rb b/lib/split/dashboard.rb index 9d57720f..fcb53c2b 100755 --- a/lib/split/dashboard.rb +++ b/lib/split/dashboard.rb @@ -1,10 +1,10 @@ # frozen_string_literal: true -require 'sinatra/base' -require 'split' -require 'bigdecimal' -require 'split/dashboard/helpers' -require 'split/dashboard/pagination_helpers' +require "sinatra/base" +require "split" +require "bigdecimal" +require "split/dashboard/helpers" +require "split/dashboard/pagination_helpers" module Split class Dashboard < Sinatra::Base @@ -18,7 +18,7 @@ class Dashboard < Sinatra::Base helpers Split::DashboardHelpers helpers Split::DashboardPaginationHelpers - get '/' do + get "/" do # Display experiments without a winner at the top of the dashboard @experiments = Split::ExperimentCatalog.all_active_first @unintialized_experiments = Split.configuration.experiments.keys - @experiments.map(&:name) @@ -26,7 +26,7 @@ class Dashboard < Sinatra::Base @metrics = Split::Metric.all # Display Rails Environment mode (or Rack version if not using Rails) - if Object.const_defined?('Rails') + if Object.const_defined?("Rails") @current_env = Rails.env.titlecase else @current_env = "Rack: #{Rack.version}" @@ -34,48 +34,48 @@ class Dashboard < Sinatra::Base erb :index end - post '/initialize_experiment' do + post "/initialize_experiment" do Split::ExperimentCatalog.find_or_create(params[:experiment]) unless params[:experiment].nil? || params[:experiment].empty? - redirect url('/') + redirect url("/") end - post '/force_alternative' do + post "/force_alternative" do experiment = Split::ExperimentCatalog.find(params[:experiment]) alternative = Split::Alternative.new(params[:alternative], experiment.name) - cookies = JSON.parse(request.cookies['split_override']) rescue {} + cookies = JSON.parse(request.cookies["split_override"]) rescue {} cookies[experiment.name] = alternative.name - response.set_cookie('split_override', { value: cookies.to_json, path: '/' }) + response.set_cookie("split_override", { value: cookies.to_json, path: "/" }) - redirect url('/') + redirect url("/") end - post '/experiment' do + post "/experiment" do @experiment = Split::ExperimentCatalog.find(params[:experiment]) @alternative = Split::Alternative.new(params[:alternative], params[:experiment]) @experiment.winner = @alternative.name - redirect url('/') + redirect url("/") end - post '/start' do + post "/start" do @experiment = Split::ExperimentCatalog.find(params[:experiment]) @experiment.start - redirect url('/') + redirect url("/") end - post '/reset' do + post "/reset" do @experiment = Split::ExperimentCatalog.find(params[:experiment]) @experiment.reset - redirect url('/') + redirect url("/") end - post '/reopen' do + post "/reopen" do @experiment = Split::ExperimentCatalog.find(params[:experiment]) @experiment.reset_winner - redirect url('/') + redirect url("/") end - post '/update_cohorting' do + post "/update_cohorting" do @experiment = Split::ExperimentCatalog.find(params[:experiment]) case params[:cohorting_action].downcase when "enable" @@ -83,13 +83,13 @@ class Dashboard < Sinatra::Base when "disable" @experiment.disable_cohorting end - redirect url('/') + redirect url("/") end - delete '/experiment' do + delete "/experiment" do @experiment = Split::ExperimentCatalog.find(params[:experiment]) @experiment.delete - redirect url('/') + redirect url("/") end end end diff --git a/lib/split/dashboard/helpers.rb b/lib/split/dashboard/helpers.rb index 71a0d93f..15727c72 100644 --- a/lib/split/dashboard/helpers.rb +++ b/lib/split/dashboard/helpers.rb @@ -7,11 +7,11 @@ def h(text) end def url(*path_parts) - [ path_prefix, path_parts ].join("/").squeeze('/') + [ path_prefix, path_parts ].join("/").squeeze("/") end def path_prefix - request.env['SCRIPT_NAME'] + request.env["SCRIPT_NAME"] end def number_to_percentage(number, precision = 2) @@ -32,13 +32,13 @@ def confidence_level(z_score) z = round(z_score.to_s.to_f, 3).abs if z >= 2.58 - '99% confidence' + "99% confidence" elsif z >= 1.96 - '95% confidence' + "95% confidence" elsif z >= 1.65 - '90% confidence' + "90% confidence" else - 'Insufficient confidence' + "Insufficient confidence" end end end diff --git a/lib/split/dashboard/pagination_helpers.rb b/lib/split/dashboard/pagination_helpers.rb index 5031135e..79d6393d 100644 --- a/lib/split/dashboard/pagination_helpers.rb +++ b/lib/split/dashboard/pagination_helpers.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require 'split/dashboard/paginator' +require "split/dashboard/paginator" module Split module DashboardPaginationHelpers @@ -43,7 +43,7 @@ def show_first_ellipsis_tag? end def ellipsis_tag - '...' + "..." end def show_prev_page_tag? diff --git a/lib/split/experiment.rb b/lib/split/experiment.rb index 6239c50e..ec78fae1 100644 --- a/lib/split/experiment.rb +++ b/lib/split/experiment.rb @@ -119,7 +119,7 @@ def algorithm=(algorithm) end def resettable=(resettable) - @resettable = resettable.is_a?(String) ? resettable == 'true' : resettable + @resettable = resettable.is_a?(String) ? resettable == "true" : resettable end def alternatives=(alts) @@ -260,8 +260,8 @@ def load_from_redis exp_config = redis.hgetall(experiment_config_key) options = { - resettable: exp_config['resettable'], - algorithm: exp_config['algorithm'], + resettable: exp_config["resettable"], + algorithm: exp_config["algorithm"], alternatives: load_alternatives_from_redis, goals: Split::GoalsCollection.new(@name).load_from_redis, metadata: load_metadata_from_redis @@ -396,7 +396,7 @@ def jstring(goal = nil) else name + "-" + goal end - js_id.gsub('/', '--') + js_id.gsub("/", "--") end def cohorting_disabled? diff --git a/lib/split/experiment_catalog.rb b/lib/split/experiment_catalog.rb index 052857de..7a8674cc 100644 --- a/lib/split/experiment_catalog.rb +++ b/lib/split/experiment_catalog.rb @@ -25,7 +25,7 @@ def self.find_or_initialize(metric_descriptor, control = nil, *alternatives) end experiment_name_with_version, goals = normalize_experiment(metric_descriptor) - experiment_name = experiment_name_with_version.to_s.split(':')[0] + experiment_name = experiment_name_with_version.to_s.split(":")[0] Split::Experiment.new(experiment_name, alternatives: [control].compact + alternatives, goals: goals) end diff --git a/lib/split/extensions/string.rb b/lib/split/extensions/string.rb index 8491c85a..e20de92c 100644 --- a/lib/split/extensions/string.rb +++ b/lib/split/extensions/string.rb @@ -4,7 +4,7 @@ class String # Constatntize is often provided by ActiveSupport, but ActiveSupport is not a dependency of Split. unless method_defined?(:constantize) def constantize - names = self.split('::') + names = self.split("::") names.shift if names.empty? || names.first.empty? constant = Object diff --git a/lib/split/goals_collection.rb b/lib/split/goals_collection.rb index 10b15bac..8aa787a4 100644 --- a/lib/split/goals_collection.rb +++ b/lib/split/goals_collection.rb @@ -28,7 +28,7 @@ def save def validate! unless @goals.nil? || @goals.kind_of?(Array) - raise ArgumentError, 'Goals must be an array' + raise ArgumentError, "Goals must be an array" end end diff --git a/lib/split/helper.rb b/lib/split/helper.rb index 0a5700e5..ae0010ad 100644 --- a/lib/split/helper.rb +++ b/lib/split/helper.rb @@ -127,14 +127,14 @@ def override_alternative_by_params(experiment_name) def override_alternative_by_cookies(experiment_name) return unless defined?(request) - if request.cookies && request.cookies.key?('split_override') - experiments = JSON.parse(request.cookies['split_override']) rescue {} + if request.cookies && request.cookies.key?("split_override") + experiments = JSON.parse(request.cookies["split_override"]) rescue {} experiments[experiment_name] end end def split_generically_disabled? - defined?(params) && params['SPLIT_DISABLE'] + defined?(params) && params["SPLIT_DISABLE"] end def ab_user @@ -150,7 +150,7 @@ def is_robot? end def is_preview? - defined?(request) && defined?(request.headers) && request.headers['x-purpose'] == 'preview' + defined?(request) && defined?(request.headers) && request.headers["x-purpose"] == "preview" end def is_ignored_ip_address? diff --git a/lib/split/metric.rb b/lib/split/metric.rb index ab67227b..ed9e48ac 100644 --- a/lib/split/metric.rb +++ b/lib/split/metric.rb @@ -16,7 +16,7 @@ def initialize(attrs = {}) def self.load_from_redis(name) metric = Split.redis.hget(:metrics, name) if metric - experiment_names = metric.split(',') + experiment_names = metric.split(",") experiments = experiment_names.collect do |experiment_name| Split::ExperimentCatalog.find(experiment_name) @@ -77,7 +77,7 @@ def self.possible_experiments(metric_name) end def save - Split.redis.hset(:metrics, name, experiments.map(&:name).join(',')) + Split.redis.hset(:metrics, name, experiments.map(&:name).join(",")) end def complete! diff --git a/lib/split/persistence.rb b/lib/split/persistence.rb index cb14e681..f9e52b33 100644 --- a/lib/split/persistence.rb +++ b/lib/split/persistence.rb @@ -2,10 +2,10 @@ module Split module Persistence - require 'split/persistence/cookie_adapter' - require 'split/persistence/dual_adapter' - require 'split/persistence/redis_adapter' - require 'split/persistence/session_adapter' + require "split/persistence/cookie_adapter" + require "split/persistence/dual_adapter" + require "split/persistence/redis_adapter" + require "split/persistence/session_adapter" ADAPTERS = { cookie: Split::Persistence::CookieAdapter, diff --git a/lib/split/persistence/cookie_adapter.rb b/lib/split/persistence/cookie_adapter.rb index 3157ce87..2eac7fca 100644 --- a/lib/split/persistence/cookie_adapter.rb +++ b/lib/split/persistence/cookie_adapter.rb @@ -43,7 +43,7 @@ def set_cookie(value = {}) end def default_options - { expires: @expires, path: '/', domain: cookie_domain_config }.compact + { expires: @expires, path: "/", domain: cookie_domain_config }.compact end def set_cookie_via_rack(key, value) @@ -53,9 +53,9 @@ def set_cookie_via_rack(key, value) # Use Rack::Utils#make_delete_cookie_header after Rack 2.0.0 def delete_cookie_header!(header, key, value) - cookie_header = header['Set-Cookie'] + cookie_header = header["Set-Cookie"] case cookie_header - when nil, '' + when nil, "" cookies = [] when String cookies = cookie_header.split("\n") @@ -64,7 +64,7 @@ def delete_cookie_header!(header, key, value) end cookies.reject! { |cookie| cookie =~ /\A#{Rack::Utils.escape(key)}=/ } - header['Set-Cookie'] = cookies.join("\n") + header["Set-Cookie"] = cookies.join("\n") end def hash diff --git a/lib/split/persistence/dual_adapter.rb b/lib/split/persistence/dual_adapter.rb index 82acac42..af8bd683 100644 --- a/lib/split/persistence/dual_adapter.rb +++ b/lib/split/persistence/dual_adapter.rb @@ -77,7 +77,7 @@ def decrement_participation?(old_value, value) end def decrement_participation(key, value) - Split.redis.hincrby("#{key}:#{value}", 'participant_count', -1) + Split.redis.hincrby("#{key}:#{value}", "participant_count", -1) end end end diff --git a/lib/split/persistence/redis_adapter.rb b/lib/split/persistence/redis_adapter.rb index 52cc0342..e314d882 100644 --- a/lib/split/persistence/redis_adapter.rb +++ b/lib/split/persistence/redis_adapter.rb @@ -3,7 +3,7 @@ module Split module Persistence class RedisAdapter - DEFAULT_CONFIG = { namespace: 'persistence' }.freeze + DEFAULT_CONFIG = { namespace: "persistence" }.freeze attr_reader :redis_key diff --git a/lib/split/user.rb b/lib/split/user.rb index 24ca4ab1..9ba7881e 100644 --- a/lib/split/user.rb +++ b/lib/split/user.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require 'forwardable' +require "forwardable" module Split class User @@ -26,10 +26,10 @@ def cleanup_old_experiments! end def max_experiments_reached?(experiment_key) - if Split.configuration.allow_multiple_experiments == 'control' + if Split.configuration.allow_multiple_experiments == "control" experiments = active_experiments experiment_key_without_version = key_without_version(experiment_key) - count_control = experiments.count { |k, v| k == experiment_key_without_version || v == 'control' } + count_control = experiments.count { |k, v| k == experiment_key_without_version || v == "control" } experiments.size > count_control else !Split.configuration.allow_multiple_experiments && diff --git a/spec/algorithms/block_randomization_spec.rb b/spec/algorithms/block_randomization_spec.rb index 3bab3fb5..9417710c 100644 --- a/spec/algorithms/block_randomization_spec.rb +++ b/spec/algorithms/block_randomization_spec.rb @@ -3,10 +3,10 @@ require "spec_helper" describe Split::Algorithms::BlockRandomization do - let(:experiment) { Split::Experiment.new 'experiment' } - let(:alternative_A) { Split::Alternative.new 'A', 'experiment' } - let(:alternative_B) { Split::Alternative.new 'B', 'experiment' } - let(:alternative_C) { Split::Alternative.new 'C', 'experiment' } + let(:experiment) { Split::Experiment.new "experiment" } + let(:alternative_A) { Split::Alternative.new "A", "experiment" } + let(:alternative_B) { Split::Alternative.new "B", "experiment" } + let(:alternative_C) { Split::Alternative.new "C", "experiment" } before :each do allow(experiment).to receive(:alternatives) { [alternative_A, alternative_B, alternative_C] } diff --git a/spec/algorithms/weighted_sample_spec.rb b/spec/algorithms/weighted_sample_spec.rb index cf62d40a..53a20170 100644 --- a/spec/algorithms/weighted_sample_spec.rb +++ b/spec/algorithms/weighted_sample_spec.rb @@ -4,17 +4,17 @@ describe Split::Algorithms::WeightedSample do it "should return an alternative" do - experiment = Split::ExperimentCatalog.find_or_create('link_color', { 'blue' => 100 }, { 'red' => 0 }) + experiment = Split::ExperimentCatalog.find_or_create("link_color", { "blue" => 100 }, { "red" => 0 }) expect(Split::Algorithms::WeightedSample.choose_alternative(experiment).class).to eq(Split::Alternative) end it "should always return a heavily weighted option" do - experiment = Split::ExperimentCatalog.find_or_create('link_color', { 'blue' => 100 }, { 'red' => 0 }) - expect(Split::Algorithms::WeightedSample.choose_alternative(experiment).name).to eq('blue') + experiment = Split::ExperimentCatalog.find_or_create("link_color", { "blue" => 100 }, { "red" => 0 }) + expect(Split::Algorithms::WeightedSample.choose_alternative(experiment).name).to eq("blue") end it "should return one of the results" do - experiment = Split::ExperimentCatalog.find_or_create('link_color', { 'blue' => 1 }, { 'red' => 1 }) - expect(['red', 'blue']).to include Split::Algorithms::WeightedSample.choose_alternative(experiment).name + experiment = Split::ExperimentCatalog.find_or_create("link_color", { "blue" => 1 }, { "red" => 1 }) + expect(["red", "blue"]).to include Split::Algorithms::WeightedSample.choose_alternative(experiment).name end end diff --git a/spec/algorithms/whiplash_spec.rb b/spec/algorithms/whiplash_spec.rb index 6a5c1c26..2d8b5291 100644 --- a/spec/algorithms/whiplash_spec.rb +++ b/spec/algorithms/whiplash_spec.rb @@ -4,13 +4,13 @@ describe Split::Algorithms::Whiplash do it "should return an algorithm" do - experiment = Split::ExperimentCatalog.find_or_create('link_color', { 'blue' => 1 }, { 'red' => 1 }) + experiment = Split::ExperimentCatalog.find_or_create("link_color", { "blue" => 1 }, { "red" => 1 }) expect(Split::Algorithms::Whiplash.choose_alternative(experiment).class).to eq(Split::Alternative) end it "should return one of the results" do - experiment = Split::ExperimentCatalog.find_or_create('link_color', { 'blue' => 1 }, { 'red' => 1 }) - expect(['red', 'blue']).to include Split::Algorithms::Whiplash.choose_alternative(experiment).name + experiment = Split::ExperimentCatalog.find_or_create("link_color", { "blue" => 1 }, { "red" => 1 }) + expect(["red", "blue"]).to include Split::Algorithms::Whiplash.choose_alternative(experiment).name end it "should guess floats" do diff --git a/spec/alternative_spec.rb b/spec/alternative_spec.rb index e29138f4..1714c2fb 100644 --- a/spec/alternative_spec.rb +++ b/spec/alternative_spec.rb @@ -1,15 +1,15 @@ # frozen_string_literal: true -require 'spec_helper' -require 'split/alternative' +require "spec_helper" +require "split/alternative" describe Split::Alternative do let(:alternative) { - Split::Alternative.new('Basket', 'basket_text') + Split::Alternative.new("Basket", "basket_text") } let(:alternative2) { - Split::Alternative.new('Cart', 'basket_text') + Split::Alternative.new("Cart", "basket_text") } let!(:experiment) { @@ -24,18 +24,18 @@ end it "should have and only return the name" do - expect(alternative.name).to eq('Basket') + expect(alternative.name).to eq("Basket") end - describe 'weights' do + describe "weights" do it "should set the weights" do - experiment = Split::Experiment.new('basket_text', alternatives: [{ 'Basket' => 0.6 }, { "Cart" => 0.4 }]) + experiment = Split::Experiment.new("basket_text", alternatives: [{ "Basket" => 0.6 }, { "Cart" => 0.4 }]) first = experiment.alternatives[0] - expect(first.name).to eq('Basket') + expect(first.name).to eq("Basket") expect(first.weight).to eq(0.6) second = experiment.alternatives[1] - expect(second.name).to eq('Cart') + expect(second.name).to eq("Cart") expect(second.weight).to eq(0.4) end @@ -51,11 +51,11 @@ } experiment = Split::Experiment.new(:my_experiment) first = experiment.alternatives[0] - expect(first.name).to eq('control_opt') + expect(first.name).to eq("control_opt") expect(first.weight).to eq(0.67) second = experiment.alternatives[1] - expect(second.name).to eq('second_opt') + expect(second.name).to eq("second_opt") expect(second.weight).to eq(0.1) end @@ -126,7 +126,7 @@ it "should save to redis" do alternative.save - expect(Split.redis.exists?('basket_text:Basket')).to be true + expect(Split.redis.exists?("basket_text:Basket")).to be true end it "should increment participation count" do @@ -166,7 +166,7 @@ expect(alternative2.control?).to be_falsey end - describe 'unfinished_count' do + describe "unfinished_count" do it "should be difference between participant and completed counts" do alternative.increment_participation expect(alternative.unfinished_count).to eq(alternative.participant_count) @@ -182,7 +182,7 @@ end end - describe 'conversion rate' do + describe "conversion rate" do it "should be 0 if there are no conversions" do expect(alternative.completed_count).to eq(0) expect(alternative.conversion_rate).to eq(0) @@ -225,7 +225,7 @@ end end - describe 'z score' do + describe "z score" do it "should return an error string when the control has 0 people" do expect(alternative2.z_score).to eq("Needs 30+ participants.") expect(alternative2.z_score(goal1)).to eq("Needs 30+ participants.") @@ -268,9 +268,9 @@ it "should be N/A for the control" do control = experiment.control - expect(control.z_score).to eq('N/A') - expect(control.z_score(goal1)).to eq('N/A') - expect(control.z_score(goal2)).to eq('N/A') + expect(control.z_score).to eq("N/A") + expect(control.z_score(goal1)).to eq("N/A") + expect(control.z_score(goal2)).to eq("N/A") end it "should not blow up for Conversion Rates > 1" do @@ -289,7 +289,7 @@ describe "extra_info" do it "reads saved value of recorded_info in redis" do saved_recorded_info = { "key_1" => 1, "key_2" => "2" } - Split.redis.hset "#{alternative.experiment_name}:#{alternative.name}", 'recorded_info', saved_recorded_info.to_json + Split.redis.hset "#{alternative.experiment_name}:#{alternative.name}", "recorded_info", saved_recorded_info.to_json extra_info = alternative.extra_info expect(extra_info).to eql(saved_recorded_info) diff --git a/spec/cache_spec.rb b/spec/cache_spec.rb index 0630c15e..48b6cd46 100644 --- a/spec/cache_spec.rb +++ b/spec/cache_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require 'spec_helper' +require "spec_helper" describe Split::Cache do let(:namespace) { :test_namespace } @@ -9,10 +9,10 @@ before { allow(Time).to receive(:now).and_return(now) } - describe 'clear' do + describe "clear" do before { Split.configuration.cache = true } - it 'clears the cache' do + it "clears the cache" do expect(Time).to receive(:now).and_return(now).exactly(2).times Split::Cache.fetch(namespace, key) { Time.now } Split::Cache.clear @@ -20,10 +20,10 @@ end end - describe 'clear_key' do + describe "clear_key" do before { Split.configuration.cache = true } - it 'clears the cache' do + it "clears the cache" do expect(Time).to receive(:now).and_return(now).exactly(3).times Split::Cache.fetch(namespace, :key1) { Time.now } Split::Cache.fetch(namespace, :key2) { Time.now } @@ -34,37 +34,37 @@ end end - describe 'fetch' do + describe "fetch" do subject { Split::Cache.fetch(namespace, key) { Time.now } } - context 'when cache disabled' do + context "when cache disabled" do before { Split.configuration.cache = false } - it 'returns the yield' do + it "returns the yield" do expect(subject).to eql(now) end - it 'yields every time' do + it "yields every time" do expect(Time).to receive(:now).and_return(now).exactly(2).times Split::Cache.fetch(namespace, key) { Time.now } Split::Cache.fetch(namespace, key) { Time.now } end end - context 'when cache enabled' do + context "when cache enabled" do before { Split.configuration.cache = true } - it 'returns the yield' do + it "returns the yield" do expect(subject).to eql(now) end - it 'yields once' do + it "yields once" do expect(Time).to receive(:now).and_return(now).once Split::Cache.fetch(namespace, key) { Time.now } Split::Cache.fetch(namespace, key) { Time.now } end - it 'honors namespace' do + it "honors namespace" do expect(Split::Cache.fetch(:a, key) { :a }).to eql(:a) expect(Split::Cache.fetch(:b, key) { :b }).to eql(:b) @@ -72,7 +72,7 @@ expect(Split::Cache.fetch(:b, key) { :b }).to eql(:b) end - it 'honors key' do + it "honors key" do expect(Split::Cache.fetch(namespace, :a) { :a }).to eql(:a) expect(Split::Cache.fetch(namespace, :b) { :b }).to eql(:b) diff --git a/spec/combined_experiments_helper_spec.rb b/spec/combined_experiments_helper_spec.rb index 008b5314..651b5649 100644 --- a/spec/combined_experiments_helper_spec.rb +++ b/spec/combined_experiments_helper_spec.rb @@ -1,12 +1,12 @@ # frozen_string_literal: true -require 'spec_helper' -require 'split/combined_experiments_helper' +require "spec_helper" +require "split/combined_experiments_helper" describe Split::CombinedExperimentsHelper do include Split::CombinedExperimentsHelper - describe 'ab_combined_test' do + describe "ab_combined_test" do let!(:config_enabled) { true } let!(:combined_experiments) { [:exp_1_click, :exp_1_scroll ] } let!(:allow_multiple_experiments) { true } @@ -23,7 +23,7 @@ Split.configuration.allow_multiple_experiments = allow_multiple_experiments end - context 'without config enabled' do + context "without config enabled" do let!(:config_enabled) { false } it "raises an error" do @@ -31,7 +31,7 @@ end end - context 'multiple experiments disabled' do + context "multiple experiments disabled" do let!(:allow_multiple_experiments) { false } it "raises an error if multiple experiments is disabled" do @@ -39,7 +39,7 @@ end end - context 'without combined experiments' do + context "without combined experiments" do let!(:combined_experiments) { nil } it "raises an error" do @@ -52,7 +52,7 @@ expect(self).to receive(:ab_test).with(:exp_1_click, { "control"=>0.5 }, { "test-alt"=>0.5 }) { "test-alt" } expect(self).to receive(:ab_test).with(:exp_1_scroll, [{ "control" => 0, "test-alt" => 1 }]) - expect(ab_combined_test('combined_exp_1')).to eq('test-alt') + expect(ab_combined_test("combined_exp_1")).to eq("test-alt") end end end diff --git a/spec/configuration_spec.rb b/spec/configuration_spec.rb index 327edb91..28c8ff91 100644 --- a/spec/configuration_spec.rb +++ b/spec/configuration_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require 'spec_helper' +require "spec_helper" describe Split::Configuration do before(:each) { @config = Split::Configuration.new } @@ -84,7 +84,7 @@ @config.experiments = YAML.load(experiments_yaml) end - it 'should normalize experiments' do + it "should normalize experiments" do expect(@config.normalized_experiments).to eq({ my_experiment: { resettable: false, alternatives: ["Control Opt", ["Alt One", "Alt Two"]] } }) end end @@ -112,10 +112,10 @@ @config.experiments = YAML.load(experiments_yaml) end - it 'should have metadata on the experiment' do + it "should have metadata on the experiment" do meta = @config.normalized_experiments[:my_experiment][:metadata] expect(meta).to_not be nil - expect(meta['Control Opt']['text']).to eq('Control Option') + expect(meta["Control Opt"]["text"]).to eq("Control Option") end end @@ -175,7 +175,7 @@ let(:yaml) { YAML.load(input) } context "with an empty string" do - let(:input) { '' } + let(:input) { "" } it "should raise an error" do expect { @config.experiments = yaml }.to raise_error(Split::InvalidExperimentsFormatError) @@ -183,7 +183,7 @@ end context "with just the YAML header" do - let(:input) { '---' } + let(:input) { "---" } it "should raise an error" do expect { @config.experiments = yaml }.to raise_error(Split::InvalidExperimentsFormatError) @@ -209,10 +209,10 @@ context "redis configuration" do it "should default to local redis server" do - old_redis_url = ENV['REDIS_URL'] - ENV.delete('REDIS_URL') + old_redis_url = ENV["REDIS_URL"] + ENV.delete("REDIS_URL") expect(Split::Configuration.new.redis).to eq("redis://localhost:6379") - ENV['REDIS_URL'] = old_redis_url + ENV["REDIS_URL"] = old_redis_url end it "should allow for redis url to be configured" do @@ -222,10 +222,10 @@ context "provided REDIS_URL environment variable" do it "should use the ENV variable" do - old_redis_url = ENV['REDIS_URL'] - ENV['REDIS_URL'] = "env_redis_url" + old_redis_url = ENV["REDIS_URL"] + ENV["REDIS_URL"] = "env_redis_url" expect(Split::Configuration.new.redis).to eq("env_redis_url") - ENV['REDIS_URL'] = old_redis_url + ENV["REDIS_URL"] = old_redis_url end end end @@ -247,8 +247,8 @@ end it "should allow the persistence cookie domain to be configured" do - @config.persistence_cookie_domain = '.acme.com' - expect(@config.persistence_cookie_domain).to eq('.acme.com') + @config.persistence_cookie_domain = ".acme.com" + expect(@config.persistence_cookie_domain).to eq(".acme.com") end end end diff --git a/spec/dashboard/pagination_helpers_spec.rb b/spec/dashboard/pagination_helpers_spec.rb index c9a360bd..cf5d81db 100644 --- a/spec/dashboard/pagination_helpers_spec.rb +++ b/spec/dashboard/pagination_helpers_spec.rb @@ -1,110 +1,110 @@ # frozen_string_literal: true -require 'spec_helper' -require 'split/dashboard/pagination_helpers' +require "spec_helper" +require "split/dashboard/pagination_helpers" describe Split::DashboardPaginationHelpers do include Split::DashboardPaginationHelpers - let(:url) { '/split/' } + let(:url) { "/split/" } - describe '#pagination_per' do - context 'when params empty' do + describe "#pagination_per" do + context "when params empty" do let(:params) { Hash[] } - it 'returns the default (10)' do + it "returns the default (10)" do default_per_page = Split.configuration.dashboard_pagination_default_per_page expect(pagination_per).to eql default_per_page expect(pagination_per).to eql 10 end end - context 'when params[:per] is 5' do + context "when params[:per] is 5" do let(:params) { Hash[per: 5] } - it 'returns 5' do + it "returns 5" do expect(pagination_per).to eql 5 end end end - describe '#page_number' do - context 'when params empty' do + describe "#page_number" do + context "when params empty" do let(:params) { Hash[] } - it 'returns 1' do + it "returns 1" do expect(page_number).to eql 1 end end context 'when params[:page] is "2"' do - let(:params) { Hash[page: '2'] } + let(:params) { Hash[page: "2"] } - it 'returns 2' do + it "returns 2" do expect(page_number).to eql 2 end end end - describe '#paginated' do + describe "#paginated" do let(:collection) { (1..20).to_a } - let(:params) { Hash[per: '5', page: '3'] } + let(:params) { Hash[per: "5", page: "3"] } it { expect(paginated(collection)).to eql [11, 12, 13, 14, 15] } end - describe '#show_first_page_tag?' do - context 'when page is 1' do + describe "#show_first_page_tag?" do + context "when page is 1" do it { expect(show_first_page_tag?).to be false } end - context 'when page is 3' do - let(:params) { Hash[page: '3'] } + context "when page is 3" do + let(:params) { Hash[page: "3"] } it { expect(show_first_page_tag?).to be true } end end - describe '#first_page_tag' do + describe "#first_page_tag" do it { expect(first_page_tag).to eql '1' } end - describe '#show_first_ellipsis_tag?' do - context 'when page is 1' do + describe "#show_first_ellipsis_tag?" do + context "when page is 1" do it { expect(show_first_ellipsis_tag?).to be false } end - context 'when page is 4' do - let(:params) { Hash[page: '4'] } + context "when page is 4" do + let(:params) { Hash[page: "4"] } it { expect(show_first_ellipsis_tag?).to be true } end end - describe '#ellipsis_tag' do - it { expect(ellipsis_tag).to eql '...' } + describe "#ellipsis_tag" do + it { expect(ellipsis_tag).to eql "..." } end - describe '#show_prev_page_tag?' do - context 'when page is 1' do + describe "#show_prev_page_tag?" do + context "when page is 1" do it { expect(show_prev_page_tag?).to be false } end - context 'when page is 2' do - let(:params) { Hash[page: '2'] } + context "when page is 2" do + let(:params) { Hash[page: "2"] } it { expect(show_prev_page_tag?).to be true } end end - describe '#prev_page_tag' do - context 'when page is 2' do - let(:params) { Hash[page: '2'] } + describe "#prev_page_tag" do + context "when page is 2" do + let(:params) { Hash[page: "2"] } it do expect(prev_page_tag).to eql '1' end end - context 'when page is 3' do - let(:params) { Hash[page: '3'] } + context "when page is 3" do + let(:params) { Hash[page: "3"] } it do expect(prev_page_tag).to eql '2' @@ -112,90 +112,90 @@ end end - describe '#show_prev_page_tag?' do - context 'when page is 1' do + describe "#show_prev_page_tag?" do + context "when page is 1" do it { expect(show_prev_page_tag?).to be false } end - context 'when page is 2' do - let(:params) { Hash[page: '2'] } + context "when page is 2" do + let(:params) { Hash[page: "2"] } it { expect(show_prev_page_tag?).to be true } end end - describe '#current_page_tag' do - context 'when page is 1' do - let(:params) { Hash[page: '1'] } - it { expect(current_page_tag).to eql '1' } + describe "#current_page_tag" do + context "when page is 1" do + let(:params) { Hash[page: "1"] } + it { expect(current_page_tag).to eql "1" } end - context 'when page is 2' do - let(:params) { Hash[page: '2'] } - it { expect(current_page_tag).to eql '2' } + context "when page is 2" do + let(:params) { Hash[page: "2"] } + it { expect(current_page_tag).to eql "2" } end end - describe '#show_next_page_tag?' do - context 'when page is 2' do - let(:params) { Hash[page: '2'] } + describe "#show_next_page_tag?" do + context "when page is 2" do + let(:params) { Hash[page: "2"] } - context 'when collection length is 20' do + context "when collection length is 20" do let(:collection) { (1..20).to_a } it { expect(show_next_page_tag?(collection)).to be false } end - context 'when collection length is 25' do + context "when collection length is 25" do let(:collection) { (1..25).to_a } it { expect(show_next_page_tag?(collection)).to be true } end end end - describe '#next_page_tag' do - context 'when page is 1' do - let(:params) { Hash[page: '1'] } + describe "#next_page_tag" do + context "when page is 1" do + let(:params) { Hash[page: "1"] } it { expect(next_page_tag).to eql '2' } end - context 'when page is 2' do - let(:params) { Hash[page: '2'] } + context "when page is 2" do + let(:params) { Hash[page: "2"] } it { expect(next_page_tag).to eql '3' } end end - describe '#total_pages' do - context 'when collection length is 30' do + describe "#total_pages" do + context "when collection length is 30" do let(:collection) { (1..30).to_a } it { expect(total_pages(collection)).to eql 3 } end - context 'when collection length is 35' do + context "when collection length is 35" do let(:collection) { (1..35).to_a } it { expect(total_pages(collection)).to eql 4 } end end - describe '#show_last_ellipsis_tag?' do + describe "#show_last_ellipsis_tag?" do let(:collection) { (1..30).to_a } - let(:params) { Hash[per: '5', page: '2'] } + let(:params) { Hash[per: "5", page: "2"] } it { expect(show_last_ellipsis_tag?(collection)).to be true } end - describe '#show_last_page_tag?' do + describe "#show_last_page_tag?" do let(:collection) { (1..30).to_a } - context 'when page is 5/6' do - let(:params) { Hash[per: '5', page: '5'] } + context "when page is 5/6" do + let(:params) { Hash[per: "5", page: "5"] } it { expect(show_last_page_tag?(collection)).to be false } end - context 'when page is 4/6' do - let(:params) { Hash[per: '5', page: '4'] } + context "when page is 4/6" do + let(:params) { Hash[per: "5", page: "4"] } it { expect(show_last_page_tag?(collection)).to be true } end end - describe '#last_page_tag' do + describe "#last_page_tag" do let(:collection) { (1..30).to_a } it { expect(last_page_tag(collection)).to eql '3' } end diff --git a/spec/dashboard/paginator_spec.rb b/spec/dashboard/paginator_spec.rb index 2d61dbba..634a6433 100644 --- a/spec/dashboard/paginator_spec.rb +++ b/spec/dashboard/paginator_spec.rb @@ -1,35 +1,35 @@ # frozen_string_literal: true -require 'spec_helper' -require 'split/dashboard/paginator' +require "spec_helper" +require "split/dashboard/paginator" describe Split::DashboardPaginator do - context 'when collection is 1..20' do + context "when collection is 1..20" do let(:collection) { (1..20).to_a } - context 'when per 5 for page' do + context "when per 5 for page" do let(:per) { 5 } - it 'when page number is 1 result is [1, 2, 3, 4, 5]' do + it "when page number is 1 result is [1, 2, 3, 4, 5]" do result = Split::DashboardPaginator.new(collection, 1, per).paginate expect(result).to eql [1, 2, 3, 4, 5] end - it 'when page number is 2 result is [6, 7, 8, 9, 10]' do + it "when page number is 2 result is [6, 7, 8, 9, 10]" do result = Split::DashboardPaginator.new(collection, 2, per).paginate expect(result).to eql [6, 7, 8, 9, 10] end end - context 'when per 10 for page' do + context "when per 10 for page" do let(:per) { 10 } - it 'when page number is 1 result is [1..10]' do + it "when page number is 1 result is [1..10]" do result = Split::DashboardPaginator.new(collection, 1, per).paginate expect(result).to eql [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] end - it 'when page number is 2 result is [10..20]' do + it "when page number is 2 result is [10..20]" do result = Split::DashboardPaginator.new(collection, 2, per).paginate expect(result).to eql [11, 12, 13, 14, 15, 16, 17, 18, 19, 20] end diff --git a/spec/dashboard_helpers_spec.rb b/spec/dashboard_helpers_spec.rb index 7b608ead..4894db01 100644 --- a/spec/dashboard_helpers_spec.rb +++ b/spec/dashboard_helpers_spec.rb @@ -1,42 +1,42 @@ # frozen_string_literal: true -require 'spec_helper' -require 'split/dashboard/helpers' +require "spec_helper" +require "split/dashboard/helpers" include Split::DashboardHelpers describe Split::DashboardHelpers do - describe 'confidence_level' do - it 'should handle very small numbers' do - expect(confidence_level(Complex(2e-18, -0.03))).to eq('Insufficient confidence') + describe "confidence_level" do + it "should handle very small numbers" do + expect(confidence_level(Complex(2e-18, -0.03))).to eq("Insufficient confidence") end it "should consider a z-score of 1.65 <= z < 1.96 as 90% confident" do - expect(confidence_level(1.65)).to eq('90% confidence') - expect(confidence_level(1.80)).to eq('90% confidence') + expect(confidence_level(1.65)).to eq("90% confidence") + expect(confidence_level(1.80)).to eq("90% confidence") end it "should consider a z-score of 1.96 <= z < 2.58 as 95% confident" do - expect(confidence_level(1.96)).to eq('95% confidence') - expect(confidence_level(2.00)).to eq('95% confidence') + expect(confidence_level(1.96)).to eq("95% confidence") + expect(confidence_level(2.00)).to eq("95% confidence") end it "should consider a z-score of z >= 2.58 as 99% confident" do - expect(confidence_level(2.58)).to eq('99% confidence') - expect(confidence_level(3.00)).to eq('99% confidence') + expect(confidence_level(2.58)).to eq("99% confidence") + expect(confidence_level(3.00)).to eq("99% confidence") end - describe '#round' do - it 'can round number strings' do - expect(round('3.1415')).to eq BigDecimal('3.14') + describe "#round" do + it "can round number strings" do + expect(round("3.1415")).to eq BigDecimal("3.14") end - it 'can round number strings for precsion' do - expect(round('3.1415', 1)).to eq BigDecimal('3.1') + it "can round number strings for precsion" do + expect(round("3.1415", 1)).to eq BigDecimal("3.1") end - it 'can handle invalid number strings' do - expect(round('N/A')).to be_zero + it "can handle invalid number strings" do + expect(round("N/A")).to be_zero end end end diff --git a/spec/dashboard_spec.rb b/spec/dashboard_spec.rb index f6ba1137..be28ba66 100644 --- a/spec/dashboard_spec.rb +++ b/spec/dashboard_spec.rb @@ -1,8 +1,8 @@ # frozen_string_literal: true -require 'spec_helper' -require 'rack/test' -require 'split/dashboard' +require "spec_helper" +require "rack/test" +require "split/dashboard" describe Split::Dashboard do include Rack::Test::Methods @@ -10,8 +10,8 @@ class TestDashboard < Split::Dashboard include Split::Helper - get '/my_experiment' do - ab_test(params[:experiment], 'blue', 'red') + get "/my_experiment" do + ab_test(params[:experiment], "blue", "red") end end @@ -32,7 +32,7 @@ def link(color) } let(:metric) { - Split::Metric.find_or_create(name: 'testmetric', experiments: [experiment, experiment_with_goals]) + Split::Metric.find_or_create(name: "testmetric", experiments: [experiment, experiment_with_goals]) } let(:red_link) { link("red") } @@ -43,7 +43,7 @@ def link(color) end it "should respond to /" do - get '/' + get "/" expect(last_response).to be_ok end @@ -55,33 +55,33 @@ def link(color) context "experiment without goals" do it "should display a Start button" do experiment - get '/' - expect(last_response.body).to include('Start') + get "/" + expect(last_response.body).to include("Start") post "/start?experiment=#{experiment.name}" - get '/' - expect(last_response.body).to include('Reset Data') - expect(last_response.body).not_to include('Metrics:') + get "/" + expect(last_response.body).to include("Reset Data") + expect(last_response.body).not_to include("Metrics:") end end context "experiment with metrics" do it "should display the names of associated metrics" do metric - get '/' - expect(last_response.body).to include('Metrics:testmetric') + get "/" + expect(last_response.body).to include("Metrics:testmetric") end end context "with goals" do it "should display a Start button" do experiment_with_goals - get '/' - expect(last_response.body).to include('Start') + get "/" + expect(last_response.body).to include("Start") post "/start?experiment=#{experiment.name}" - get '/' - expect(last_response.body).to include('Reset Data') + get "/" + expect(last_response.body).to include("Reset Data") end end end @@ -89,7 +89,7 @@ def link(color) describe "force alternative" do context "initial version" do let!(:user) do - Split::User.new(@app, { experiment.name => 'red' }) + Split::User.new(@app, { experiment.name => "red" }) end before do @@ -116,7 +116,7 @@ def link(color) context "incremented version" do let!(:user) do experiment.increment_version - Split::User.new(@app, { "#{experiment.name}:#{experiment.version}" => 'red' }) + Split::User.new(@app, { "#{experiment.name}:#{experiment.version}" => "red" }) end before do @@ -135,28 +135,28 @@ def link(color) describe "index page" do context "with winner" do - before { experiment.winner = 'red' } + before { experiment.winner = "red" } it "displays `Reopen Experiment` button" do - get '/' + get "/" - expect(last_response.body).to include('Reopen Experiment') + expect(last_response.body).to include("Reopen Experiment") end end context "without winner" do it "should not display `Reopen Experiment` button" do - get '/' + get "/" - expect(last_response.body).to_not include('Reopen Experiment') + expect(last_response.body).to_not include("Reopen Experiment") end end end describe "reopen experiment" do - before { experiment.winner = 'red' } + before { experiment.winner = "red" } - it 'redirects' do + it "redirects" do post "/reopen?experiment=#{experiment.name}" expect(last_response).to be_redirect @@ -171,7 +171,7 @@ def link(color) it "keeps existing stats" do red_link.participant_count = 5 blue_link.participant_count = 7 - experiment.winner = 'blue' + experiment.winner = "blue" post "/reopen?experiment=#{experiment.name}" @@ -236,7 +236,7 @@ def link(color) it "should reset an experiment" do red_link.participant_count = 5 blue_link.participant_count = 7 - experiment.winner = 'blue' + experiment.winner = "blue" post "/reset?experiment=#{experiment.name}" @@ -258,16 +258,16 @@ def link(color) it "should mark an alternative as the winner" do expect(experiment.winner).to be_nil - post "/experiment?experiment=#{experiment.name}", alternative: 'red' + post "/experiment?experiment=#{experiment.name}", alternative: "red" expect(last_response).to be_redirect - expect(experiment.winner.name).to eq('red') + expect(experiment.winner.name).to eq("red") end it "should display the start date" do experiment.start - get '/' + get "/" expect(last_response.body).to include("#{experiment.start_time.strftime('%Y-%m-%d')}") end @@ -275,8 +275,8 @@ def link(color) it "should handle experiments without a start date" do Split.redis.hdel(:experiment_start_times, experiment.name) - get '/' + get "/" - expect(last_response.body).to include('Unknown') + expect(last_response.body).to include("Unknown") end end diff --git a/spec/encapsulated_helper_spec.rb b/spec/encapsulated_helper_spec.rb index 32a814c3..df9dfa9a 100644 --- a/spec/encapsulated_helper_spec.rb +++ b/spec/encapsulated_helper_spec.rb @@ -1,13 +1,12 @@ # frozen_string_literal: true -require 'spec_helper' +require "spec_helper" describe Split::EncapsulatedHelper do include Split::EncapsulatedHelper - def params - raise NoMethodError, 'This method is not really defined' + raise NoMethodError, "This method is not really defined" end describe "ab_test" do @@ -18,17 +17,17 @@ def params it "should not raise an error when params raises an error" do expect { params }.to raise_error(NoMethodError) - expect { ab_test('link_color', 'blue', 'red') }.not_to raise_error + expect { ab_test("link_color", "blue", "red") }.not_to raise_error end it "calls the block with selected alternative" do - expect { |block| ab_test('link_color', 'red', 'red', &block) }.to yield_with_args('red', {}) + expect { |block| ab_test("link_color", "red", "red", &block) }.to yield_with_args("red", {}) end context "inside a view" do it "works inside ERB" do - require 'erb' - template = ERB.new(<<-ERB.split(/\s+/s).map(&:strip).join(' '), nil, "%") + require "erb" + template = ERB.new(<<-ERB.split(/\s+/s).map(&:strip).join(" "), nil, "%") foo <% ab_test(:foo, '1', '2') do |alt, meta| %> static <%= alt %> <% end %> @@ -39,13 +38,13 @@ def params end describe "context" do - it 'is passed in shim' do + it "is passed in shim" do ctx = Class.new { include Split::EncapsulatedHelper public :session }.new expect(ctx).to receive(:session) { {} } - expect { ctx.ab_test('link_color', 'blue', 'red') }.not_to raise_error + expect { ctx.ab_test("link_color", "blue", "red") }.not_to raise_error end end end diff --git a/spec/experiment_catalog_spec.rb b/spec/experiment_catalog_spec.rb index 6f5b0531..192d0ffc 100644 --- a/spec/experiment_catalog_spec.rb +++ b/spec/experiment_catalog_spec.rb @@ -1,54 +1,54 @@ # frozen_string_literal: true -require 'spec_helper' +require "spec_helper" describe Split::ExperimentCatalog do subject { Split::ExperimentCatalog } describe ".find_or_create" do it "should not raise an error when passed strings for alternatives" do - expect { subject.find_or_create('xyz', '1', '2', '3') }.not_to raise_error + expect { subject.find_or_create("xyz", "1", "2", "3") }.not_to raise_error end it "should not raise an error when passed an array for alternatives" do - expect { subject.find_or_create('xyz', ['1', '2', '3']) }.not_to raise_error + expect { subject.find_or_create("xyz", ["1", "2", "3"]) }.not_to raise_error end it "should raise the appropriate error when passed integers for alternatives" do - expect { subject.find_or_create('xyz', 1, 2, 3) }.to raise_error(ArgumentError) + expect { subject.find_or_create("xyz", 1, 2, 3) }.to raise_error(ArgumentError) end it "should raise the appropriate error when passed symbols for alternatives" do - expect { subject.find_or_create('xyz', :a, :b, :c) }.to raise_error(ArgumentError) + expect { subject.find_or_create("xyz", :a, :b, :c) }.to raise_error(ArgumentError) end it "should not raise error when passed an array for goals" do - expect { subject.find_or_create({ 'link_color' => ["purchase", "refund"] }, 'blue', 'red') } + expect { subject.find_or_create({ "link_color" => ["purchase", "refund"] }, "blue", "red") } .not_to raise_error end it "should not raise error when passed just one goal" do - expect { subject.find_or_create({ 'link_color' => "purchase" }, 'blue', 'red') } + expect { subject.find_or_create({ "link_color" => "purchase" }, "blue", "red") } .not_to raise_error end it "constructs a new experiment" do - expect(subject.find_or_create('my_exp', 'control me').control.to_s).to eq('control me') + expect(subject.find_or_create("my_exp", "control me").control.to_s).to eq("control me") end end - describe '.find' do + describe ".find" do it "should return an existing experiment" do - experiment = Split::Experiment.new('basket_text', alternatives: ['blue', 'red', 'green']) + experiment = Split::Experiment.new("basket_text", alternatives: ["blue", "red", "green"]) experiment.save - experiment = subject.find('basket_text') + experiment = subject.find("basket_text") - expect(experiment.name).to eq('basket_text') + expect(experiment.name).to eq("basket_text") end it "should return nil if experiment not exist" do - expect(subject.find('non_existent_experiment')).to be_nil + expect(subject.find("non_existent_experiment")).to be_nil end end end diff --git a/spec/experiment_spec.rb b/spec/experiment_spec.rb index 93ab55d2..78e930ca 100644 --- a/spec/experiment_spec.rb +++ b/spec/experiment_spec.rb @@ -1,15 +1,15 @@ # frozen_string_literal: true -require 'spec_helper' -require 'time' +require "spec_helper" +require "time" describe Split::Experiment do def new_experiment(goals = []) - Split::Experiment.new('link_color', alternatives: ['blue', 'red', 'green'], goals: goals) + Split::Experiment.new("link_color", alternatives: ["blue", "red", "green"], goals: goals) end def alternative(color) - Split::Alternative.new(color, 'link_color') + Split::Alternative.new(color, "link_color") end let(:experiment) { new_experiment } @@ -18,10 +18,10 @@ def alternative(color) let(:green) { alternative("green") } context "with an experiment" do - let(:experiment) { Split::Experiment.new('basket_text', alternatives: ['Basket', "Cart"]) } + let(:experiment) { Split::Experiment.new("basket_text", alternatives: ["Basket", "Cart"]) } it "should have a name" do - expect(experiment.name).to eq('basket_text') + expect(experiment.name).to eq("basket_text") end it "should have alternatives" do @@ -29,7 +29,7 @@ def alternative(color) end it "should have alternatives with correct names" do - expect(experiment.alternatives.collect { |a| a.name }).to eq(['Basket', 'Cart']) + expect(experiment.alternatives.collect { |a| a.name }).to eq(["Basket", "Cart"]) end it "should be resettable by default" do @@ -38,7 +38,7 @@ def alternative(color) it "should save to redis" do experiment.save - expect(Split.redis.exists?('basket_text')).to be true + expect(Split.redis.exists?("basket_text")).to be true end it "should save the start time to redis" do @@ -46,14 +46,14 @@ def alternative(color) expect(Time).to receive(:now).and_return(experiment_start_time) experiment.save - expect(Split::ExperimentCatalog.find('basket_text').start_time).to eq(experiment_start_time) + expect(Split::ExperimentCatalog.find("basket_text").start_time).to eq(experiment_start_time) end it "should not save the start time to redis when start_manually is enabled" do expect(Split.configuration).to receive(:start_manually).and_return(true) experiment.save - expect(Split::ExperimentCatalog.find('basket_text').start_time).to be_nil + expect(Split::ExperimentCatalog.find("basket_text").start_time).to be_nil end it "should save the selected algorithm to redis" do @@ -61,7 +61,7 @@ def alternative(color) experiment.algorithm = experiment_algorithm experiment.save - expect(Split::ExperimentCatalog.find('basket_text').algorithm).to eq(experiment_algorithm) + expect(Split::ExperimentCatalog.find("basket_text").algorithm).to eq(experiment_algorithm) end it "should handle having a start time stored as a string" do @@ -70,7 +70,7 @@ def alternative(color) experiment.save Split.redis.hset(:experiment_start_times, experiment.name, experiment_start_time) - expect(Split::ExperimentCatalog.find('basket_text').start_time).to eq(experiment_start_time) + expect(Split::ExperimentCatalog.find("basket_text").start_time).to eq(experiment_start_time) end it "should handle not having a start time" do @@ -80,17 +80,17 @@ def alternative(color) Split.redis.hdel(:experiment_start_times, experiment.name) - expect(Split::ExperimentCatalog.find('basket_text').start_time).to be_nil + expect(Split::ExperimentCatalog.find("basket_text").start_time).to be_nil end it "should not create duplicates when saving multiple times" do experiment.save experiment.save - expect(Split.redis.exists?('basket_text')).to be true - expect(Split.redis.lrange('basket_text', 0, -1)).to eq(['{"Basket":1}', '{"Cart":1}']) + expect(Split.redis.exists?("basket_text")).to be true + expect(Split.redis.lrange("basket_text", 0, -1)).to eq(['{"Basket":1}', '{"Cart":1}']) end - describe 'new record?' do + describe "new record?" do it "should know if it hasn't been saved yet" do expect(experiment.new_record?).to be_truthy end @@ -101,56 +101,56 @@ def alternative(color) end end - describe 'control' do - it 'should be the first alternative' do + describe "control" do + it "should be the first alternative" do experiment.save - expect(experiment.control.name).to eq('Basket') + expect(experiment.control.name).to eq("Basket") end end end - describe 'initialization' do + describe "initialization" do it "should set the algorithm when passed as an option to the initializer" do - experiment = Split::Experiment.new('basket_text', alternatives: ['Basket', "Cart"], algorithm: Split::Algorithms::Whiplash) + experiment = Split::Experiment.new("basket_text", alternatives: ["Basket", "Cart"], algorithm: Split::Algorithms::Whiplash) expect(experiment.algorithm).to eq(Split::Algorithms::Whiplash) end it "should be possible to make an experiment not resettable" do - experiment = Split::Experiment.new('basket_text', alternatives: ['Basket', "Cart"], resettable: false) + experiment = Split::Experiment.new("basket_text", alternatives: ["Basket", "Cart"], resettable: false) expect(experiment.resettable).to be_falsey end - context 'from configuration' do + context "from configuration" do let(:experiment_name) { :my_experiment } let(:experiments) do { experiment_name => { - alternatives: ['Control Opt', 'Alt one'] + alternatives: ["Control Opt", "Alt one"] } } end before { Split.configuration.experiments = experiments } - it 'assigns default values to the experiment' do + it "assigns default values to the experiment" do expect(Split::Experiment.new(experiment_name).resettable).to eq(true) end end end - describe 'persistent configuration' do + describe "persistent configuration" do it "should persist resettable in redis" do - experiment = Split::Experiment.new('basket_text', alternatives: ['Basket', "Cart"], resettable: false) + experiment = Split::Experiment.new("basket_text", alternatives: ["Basket", "Cart"], resettable: false) experiment.save - e = Split::ExperimentCatalog.find('basket_text') + e = Split::ExperimentCatalog.find("basket_text") expect(e).to eq(experiment) expect(e.resettable).to be_falsey end - describe '#metadata' do - let(:experiment) { Split::Experiment.new('basket_text', alternatives: ['Basket', "Cart"], algorithm: Split::Algorithms::Whiplash, metadata: meta) } - let(:meta) { { a: 'b' } } + describe "#metadata" do + let(:experiment) { Split::Experiment.new("basket_text", alternatives: ["Basket", "Cart"], algorithm: Split::Algorithms::Whiplash, metadata: meta) } + let(:meta) { { a: "b" } } before do experiment.save @@ -163,20 +163,20 @@ def alternative(color) expect(Split.redis.exists?(experiment.metadata_key)).to be_falsey end - context 'simple hash' do - let(:meta) { { 'basket' => 'a', 'cart' => 'b' } } + context "simple hash" do + let(:meta) { { "basket" => "a", "cart" => "b" } } it "should persist metadata in redis" do - e = Split::ExperimentCatalog.find('basket_text') + e = Split::ExperimentCatalog.find("basket_text") expect(e).to eq(experiment) expect(e.metadata).to eq(meta) end end - context 'nested hash' do - let(:meta) { { 'basket' => { 'one' => 'two' }, 'cart' => 'b' } } + context "nested hash" do + let(:meta) { { "basket" => { "one" => "two" }, "cart" => "b" } } it "should persist metadata in redis" do - e = Split::ExperimentCatalog.find('basket_text') + e = Split::ExperimentCatalog.find("basket_text") expect(e).to eq(experiment) expect(e.metadata).to eq(meta) end @@ -184,32 +184,32 @@ def alternative(color) end it "should persist algorithm in redis" do - experiment = Split::Experiment.new('basket_text', alternatives: ['Basket', "Cart"], algorithm: Split::Algorithms::Whiplash) + experiment = Split::Experiment.new("basket_text", alternatives: ["Basket", "Cart"], algorithm: Split::Algorithms::Whiplash) experiment.save - e = Split::ExperimentCatalog.find('basket_text') + e = Split::ExperimentCatalog.find("basket_text") expect(e).to eq(experiment) expect(e.algorithm).to eq(Split::Algorithms::Whiplash) end it "should persist a new experiment in redis, that does not exist in the configuration file" do - experiment = Split::Experiment.new('foobar', alternatives: ['tra', 'la'], algorithm: Split::Algorithms::Whiplash) + experiment = Split::Experiment.new("foobar", alternatives: ["tra", "la"], algorithm: Split::Algorithms::Whiplash) experiment.save - e = Split::ExperimentCatalog.find('foobar') + e = Split::ExperimentCatalog.find("foobar") expect(e).to eq(experiment) - expect(e.alternatives.collect { |a| a.name }).to eq(['tra', 'la']) + expect(e.alternatives.collect { |a| a.name }).to eq(["tra", "la"]) end end - describe 'deleting' do - it 'should delete itself' do - experiment = Split::Experiment.new('basket_text', alternatives: [ 'Basket', "Cart"]) + describe "deleting" do + it "should delete itself" do + experiment = Split::Experiment.new("basket_text", alternatives: [ "Basket", "Cart"]) experiment.save experiment.delete - expect(Split.redis.exists?('link_color')).to be false - expect(Split::ExperimentCatalog.find('link_color')).to be_nil + expect(Split.redis.exists?("link_color")).to be false + expect(Split::ExperimentCatalog.find("link_color")).to be_nil end it "should increment the version" do @@ -228,7 +228,7 @@ def alternative(color) experiment.delete end - it 'should reset the start time if the experiment should be manually started' do + it "should reset the start time if the experiment should be manually started" do Split.configuration.start_manually = true experiment.start experiment.delete @@ -243,75 +243,75 @@ def alternative(color) end end - describe 'winner' do + describe "winner" do it "should have no winner initially" do expect(experiment.winner).to be_nil end end - describe 'winner=' do - it 'should allow you to specify a winner' do + describe "winner=" do + it "should allow you to specify a winner" do experiment.save - experiment.winner = 'red' - expect(experiment.winner.name).to eq('red') + experiment.winner = "red" + expect(experiment.winner.name).to eq("red") end - it 'should call the on_experiment_winner_choose hook' do + it "should call the on_experiment_winner_choose hook" do expect(Split.configuration.on_experiment_winner_choose).to receive(:call) - experiment.winner = 'green' + experiment.winner = "green" end - context 'when has_winner state is memoized' do + context "when has_winner state is memoized" do before { expect(experiment).to_not have_winner } - it 'should keep has_winner state consistent' do - experiment.winner = 'red' + it "should keep has_winner state consistent" do + experiment.winner = "red" expect(experiment).to have_winner end end end - describe 'reset_winner' do - before { experiment.winner = 'green' } + describe "reset_winner" do + before { experiment.winner = "green" } - it 'should reset the winner' do + it "should reset the winner" do experiment.reset_winner expect(experiment.winner).to be_nil end - context 'when has_winner state is memoized' do + context "when has_winner state is memoized" do before { expect(experiment).to have_winner } - it 'should keep has_winner state consistent' do + it "should keep has_winner state consistent" do experiment.reset_winner expect(experiment).to_not have_winner end end end - describe 'has_winner?' do - context 'with winner' do - before { experiment.winner = 'red' } + describe "has_winner?" do + context "with winner" do + before { experiment.winner = "red" } - it 'returns true' do + it "returns true" do expect(experiment).to have_winner end end - context 'without winner' do - it 'returns false' do + context "without winner" do + it "returns false" do expect(experiment).to_not have_winner end end - it 'memoizes has_winner state' do + it "memoizes has_winner state" do expect(experiment).to receive(:winner).once expect(experiment).to_not have_winner expect(experiment).to_not have_winner end end - describe 'reset' do + describe "reset" do let(:reset_manually) { false } before do @@ -321,10 +321,10 @@ def alternative(color) green.increment_participation end - it 'should reset all alternatives' do - experiment.winner = 'green' + it "should reset all alternatives" do + experiment.winner = "green" - expect(experiment.next_alternative.name).to eq('green') + expect(experiment.next_alternative.name).to eq("green") green.increment_participation experiment.reset @@ -333,10 +333,10 @@ def alternative(color) expect(green.completed_count).to eq(0) end - it 'should reset the winner' do - experiment.winner = 'green' + it "should reset the winner" do + experiment.winner = "green" - expect(experiment.next_alternative.name).to eq('green') + expect(experiment.next_alternative.name).to eq("green") green.increment_participation experiment.reset @@ -361,50 +361,50 @@ def alternative(color) end end - describe 'algorithm' do - let(:experiment) { Split::ExperimentCatalog.find_or_create('link_color', 'blue', 'red', 'green') } + describe "algorithm" do + let(:experiment) { Split::ExperimentCatalog.find_or_create("link_color", "blue", "red", "green") } - it 'should use the default algorithm if none is specified' do + it "should use the default algorithm if none is specified" do expect(experiment.algorithm).to eq(Split.configuration.algorithm) end - it 'should use the user specified algorithm for this experiment if specified' do + it "should use the user specified algorithm for this experiment if specified" do experiment.algorithm = Split::Algorithms::Whiplash expect(experiment.algorithm).to eq(Split::Algorithms::Whiplash) end end - describe '#next_alternative' do - context 'with multiple alternatives' do - let(:experiment) { Split::ExperimentCatalog.find_or_create('link_color', 'blue', 'red', 'green') } + describe "#next_alternative" do + context "with multiple alternatives" do + let(:experiment) { Split::ExperimentCatalog.find_or_create("link_color", "blue", "red", "green") } - context 'with winner' do + context "with winner" do it "should always return the winner" do - green = Split::Alternative.new('green', 'link_color') - experiment.winner = 'green' + green = Split::Alternative.new("green", "link_color") + experiment.winner = "green" - expect(experiment.next_alternative.name).to eq('green') + expect(experiment.next_alternative.name).to eq("green") green.increment_participation - expect(experiment.next_alternative.name).to eq('green') + expect(experiment.next_alternative.name).to eq("green") end end - context 'without winner' do + context "without winner" do it "should use the specified algorithm" do experiment.algorithm = Split::Algorithms::Whiplash - expect(experiment.algorithm).to receive(:choose_alternative).and_return(Split::Alternative.new('green', 'link_color')) - expect(experiment.next_alternative.name).to eq('green') + expect(experiment.algorithm).to receive(:choose_alternative).and_return(Split::Alternative.new("green", "link_color")) + expect(experiment.next_alternative.name).to eq("green") end end end - context 'with single alternative' do - let(:experiment) { Split::ExperimentCatalog.find_or_create('link_color', 'blue') } + context "with single alternative" do + let(:experiment) { Split::ExperimentCatalog.find_or_create("link_color", "blue") } it "should always return the only alternative" do - expect(experiment.next_alternative.name).to eq('blue') - expect(experiment.next_alternative.name).to eq('blue') + expect(experiment.next_alternative.name).to eq("blue") + expect(experiment.next_alternative.name).to eq("blue") end end end @@ -425,16 +425,16 @@ def alternative(color) end end - describe 'changing an existing experiment' do + describe "changing an existing experiment" do def same_but_different_alternative - Split::ExperimentCatalog.find_or_create('link_color', 'blue', 'yellow', 'orange') + Split::ExperimentCatalog.find_or_create("link_color", "blue", "yellow", "orange") end it "should reset an experiment if it is loaded with different alternatives" do experiment.save blue.participant_count = 5 same_experiment = same_but_different_alternative - expect(same_experiment.alternatives.map(&:name)).to eq(['blue', 'yellow', 'orange']) + expect(same_experiment.alternatives.map(&:name)).to eq(["blue", "yellow", "orange"]) expect(blue.participant_count).to eq(0) end @@ -450,7 +450,7 @@ def same_but_different_alternative context "when metadata is changed" do it "should increase version" do experiment.save - experiment.metadata = { 'foo' => 'bar' } + experiment.metadata = { "foo" => "bar" } expect { experiment.save }.to change { experiment.version }.by(1) end @@ -462,7 +462,7 @@ def same_but_different_alternative end end - context 'when experiment configuration is changed' do + context "when experiment configuration is changed" do let(:reset_manually) { false } before do @@ -475,15 +475,15 @@ def same_but_different_alternative experiment.save end - it 'resets all alternatives' do + it "resets all alternatives" do expect(green.participant_count).to eq(0) expect(green.completed_count).to eq(0) end - context 'when reset_manually is set' do + context "when reset_manually is set" do let(:reset_manually) { true } - it 'does not reset alternatives' do + it "does not reset alternatives" do expect(green.participant_count).to eq(2) expect(green.completed_count).to eq(0) end @@ -491,16 +491,16 @@ def same_but_different_alternative end end - describe 'alternatives passed as non-strings' do + describe "alternatives passed as non-strings" do it "should throw an exception if an alternative is passed that is not a string" do - expect { Split::ExperimentCatalog.find_or_create('link_color', :blue, :red) }.to raise_error(ArgumentError) - expect { Split::ExperimentCatalog.find_or_create('link_enabled', true, false) }.to raise_error(ArgumentError) + expect { Split::ExperimentCatalog.find_or_create("link_color", :blue, :red) }.to raise_error(ArgumentError) + expect { Split::ExperimentCatalog.find_or_create("link_enabled", true, false) }.to raise_error(ArgumentError) end end - describe 'specifying weights' do + describe "specifying weights" do let(:experiment_with_weight) { - Split::ExperimentCatalog.find_or_create('link_color', { 'blue' => 1 }, { 'red' => 2 }) + Split::ExperimentCatalog.find_or_create("link_color", { "blue" => 1 }, { "red" => 2 }) } it "should work for a new experiment" do @@ -519,7 +519,7 @@ def same_but_different_alternative } context "saving experiment" do - let(:same_but_different_goals) { Split::ExperimentCatalog.find_or_create({ 'link_color' => ["purchase", "refund"] }, 'blue', 'red', 'green') } + let(:same_but_different_goals) { Split::ExperimentCatalog.find_or_create({ "link_color" => ["purchase", "refund"] }, "blue", "red", "green") } before { experiment.save } @@ -539,9 +539,9 @@ def same_but_different_alternative context "find or create experiment" do it "should have correct goals" do - experiment = Split::ExperimentCatalog.find_or_create({ 'link_color3' => ["purchase", "refund"] }, 'blue', 'red', 'green') + experiment = Split::ExperimentCatalog.find_or_create({ "link_color3" => ["purchase", "refund"] }, "blue", "red", "green") expect(experiment.goals).to eq(["purchase", "refund"]) - experiment = Split::ExperimentCatalog.find_or_create('link_color3', 'blue', 'red', 'green') + experiment = Split::ExperimentCatalog.find_or_create("link_color3", "blue", "red", "green") expect(experiment.goals).to eq([]) end end @@ -549,19 +549,19 @@ def same_but_different_alternative describe "beta probability calculation" do it "should return a hash with the probability of each alternative being the best" do - experiment = Split::ExperimentCatalog.find_or_create('mathematicians', 'bernoulli', 'poisson', 'lagrange') + experiment = Split::ExperimentCatalog.find_or_create("mathematicians", "bernoulli", "poisson", "lagrange") experiment.calc_winning_alternatives expect(experiment.alternative_probabilities).not_to be_nil end it "should return between 46% and 54% probability for an experiment with 2 alternatives and no data" do - experiment = Split::ExperimentCatalog.find_or_create('scientists', 'einstein', 'bohr') + experiment = Split::ExperimentCatalog.find_or_create("scientists", "einstein", "bohr") experiment.calc_winning_alternatives expect(experiment.alternatives[0].p_winner).to be_within(0.04).of(0.50) end it "should calculate the probability of being the winning alternative separately for each goal", skip: true do - experiment = Split::ExperimentCatalog.find_or_create({ 'link_color3' => ["purchase", "refund"] }, 'blue', 'red', 'green') + experiment = Split::ExperimentCatalog.find_or_create({ "link_color3" => ["purchase", "refund"] }, "blue", "red", "green") goal1 = experiment.goals[0] goal2 = experiment.goals[1] experiment.alternatives.each do |alternative| @@ -577,7 +577,7 @@ def same_but_different_alternative end it "should return nil and not re-calculate probabilities if they have already been calculated today" do - experiment = Split::ExperimentCatalog.find_or_create({ 'link_color3' => ["purchase", "refund"] }, 'blue', 'red', 'green') + experiment = Split::ExperimentCatalog.find_or_create({ "link_color3" => ["purchase", "refund"] }, "blue", "red", "green") expect(experiment.calc_winning_alternatives).not_to be nil expect(experiment.calc_winning_alternatives).to be nil end diff --git a/spec/goals_collection_spec.rb b/spec/goals_collection_spec.rb index f7ba648f..26f9d223 100644 --- a/spec/goals_collection_spec.rb +++ b/spec/goals_collection_spec.rb @@ -1,43 +1,43 @@ # frozen_string_literal: true -require 'spec_helper' -require 'split/goals_collection' -require 'time' +require "spec_helper" +require "split/goals_collection" +require "time" describe Split::GoalsCollection do - let(:experiment_name) { 'experiment_name' } + let(:experiment_name) { "experiment_name" } - describe 'initialization' do + describe "initialization" do let(:goals_collection) { - Split::GoalsCollection.new('experiment_name', ['goal1', 'goal2']) + Split::GoalsCollection.new("experiment_name", ["goal1", "goal2"]) } it "should have an experiment_name" do expect(goals_collection.instance_variable_get(:@experiment_name)). - to eq('experiment_name') + to eq("experiment_name") end it "should have a list of goals" do expect(goals_collection.instance_variable_get(:@goals)). - to eq(['goal1', 'goal2']) + to eq(["goal1", "goal2"]) end end describe "#validate!" do it "should't raise ArgumentError if @goals is nil?" do - goals_collection = Split::GoalsCollection.new('experiment_name') + goals_collection = Split::GoalsCollection.new("experiment_name") expect { goals_collection.validate! }.not_to raise_error end it "should raise ArgumentError if @goals is not an Array" do goals_collection = Split::GoalsCollection. - new('experiment_name', 'not an array') + new("experiment_name", "not an array") expect { goals_collection.validate! }.to raise_error(ArgumentError) end it "should't raise ArgumentError if @goals is an array" do goals_collection = Split::GoalsCollection. - new('experiment_name', ['an array']) + new("experiment_name", ["an array"]) expect { goals_collection.validate! }.not_to raise_error end end @@ -46,7 +46,7 @@ let(:goals_key) { "#{experiment_name}:goals" } it "should delete goals from redis" do - goals_collection = Split::GoalsCollection.new(experiment_name, ['goal1']) + goals_collection = Split::GoalsCollection.new(experiment_name, ["goal1"]) goals_collection.save goals_collection.delete @@ -65,7 +65,7 @@ end it "should save goals to redis if @goals is valid" do - goals = ['valid goal 1', 'valid goal 2'] + goals = ["valid goal 1", "valid goal 2"] collection = Split::GoalsCollection.new(experiment_name, goals) collection.save @@ -74,9 +74,9 @@ it "should return @goals if @goals is valid" do goals_collection = Split::GoalsCollection. - new(experiment_name, ['valid goal']) + new(experiment_name, ["valid goal"]) - expect(goals_collection.save).to eq(['valid goal']) + expect(goals_collection.save).to eq(["valid goal"]) end end end diff --git a/spec/helper_spec.rb b/spec/helper_spec.rb index 62a40a68..33341657 100755 --- a/spec/helper_spec.rb +++ b/spec/helper_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require 'spec_helper' +require "spec_helper" # TODO change some of these tests to use Rack::Test @@ -8,32 +8,32 @@ include Split::Helper let(:experiment) { - Split::ExperimentCatalog.find_or_create('link_color', 'blue', 'red') + Split::ExperimentCatalog.find_or_create("link_color", "blue", "red") } describe "ab_test" do it "should not raise an error when passed strings for alternatives" do - expect { ab_test('xyz', '1', '2', '3') }.not_to raise_error + expect { ab_test("xyz", "1", "2", "3") }.not_to raise_error end it "should not raise an error when passed an array for alternatives" do - expect { ab_test('xyz', ['1', '2', '3']) }.not_to raise_error + expect { ab_test("xyz", ["1", "2", "3"]) }.not_to raise_error end it "should raise the appropriate error when passed integers for alternatives" do - expect { ab_test('xyz', 1, 2, 3) }.to raise_error(ArgumentError) + expect { ab_test("xyz", 1, 2, 3) }.to raise_error(ArgumentError) end it "should raise the appropriate error when passed symbols for alternatives" do - expect { ab_test('xyz', :a, :b, :c) }.to raise_error(ArgumentError) + expect { ab_test("xyz", :a, :b, :c) }.to raise_error(ArgumentError) end it "should not raise error when passed an array for goals" do - expect { ab_test({ 'link_color' => ["purchase", "refund"] }, 'blue', 'red') }.not_to raise_error + expect { ab_test({ "link_color" => ["purchase", "refund"] }, "blue", "red") }.not_to raise_error end it "should not raise error when passed just one goal" do - expect { ab_test({ 'link_color' => "purchase" }, 'blue', 'red') }.not_to raise_error + expect { ab_test({ "link_color" => "purchase" }, "blue", "red") }.not_to raise_error end it "raises an appropriate error when processing combined expirements" do @@ -44,120 +44,120 @@ combined_experiments: [:combined_exp_1_sub_1] } } - Split::ExperimentCatalog.find_or_create('combined_exp_1') - expect { ab_test('combined_exp_1') }.to raise_error(Split::InvalidExperimentsFormatError) + Split::ExperimentCatalog.find_or_create("combined_exp_1") + expect { ab_test("combined_exp_1") }.to raise_error(Split::InvalidExperimentsFormatError) end it "should assign a random alternative to a new user when there are an equal number of alternatives assigned" do - ab_test('link_color', 'blue', 'red') - expect(['red', 'blue']).to include(ab_user['link_color']) + ab_test("link_color", "blue", "red") + expect(["red", "blue"]).to include(ab_user["link_color"]) end it "should increment the participation counter after assignment to a new user" do - previous_red_count = Split::Alternative.new('red', 'link_color').participant_count - previous_blue_count = Split::Alternative.new('blue', 'link_color').participant_count + previous_red_count = Split::Alternative.new("red", "link_color").participant_count + previous_blue_count = Split::Alternative.new("blue", "link_color").participant_count - ab_test('link_color', 'blue', 'red') + ab_test("link_color", "blue", "red") - new_red_count = Split::Alternative.new('red', 'link_color').participant_count - new_blue_count = Split::Alternative.new('blue', 'link_color').participant_count + new_red_count = Split::Alternative.new("red", "link_color").participant_count + new_blue_count = Split::Alternative.new("blue", "link_color").participant_count expect((new_red_count + new_blue_count)).to eq(previous_red_count + previous_blue_count + 1) end - it 'should not increment the counter for an experiment that the user is not participating in' do - ab_test('link_color', 'blue', 'red') - e = Split::ExperimentCatalog.find_or_create('button_size', 'small', 'big') + it "should not increment the counter for an experiment that the user is not participating in" do + ab_test("link_color", "blue", "red") + e = Split::ExperimentCatalog.find_or_create("button_size", "small", "big") expect { # User shouldn't participate in this second experiment - ab_test('button_size', 'small', 'big') + ab_test("button_size", "small", "big") }.not_to change { e.participant_count } end - it 'should not increment the counter for an ended experiment' do - e = Split::ExperimentCatalog.find_or_create('button_size', 'small', 'big') - e.winner = 'small' + it "should not increment the counter for an ended experiment" do + e = Split::ExperimentCatalog.find_or_create("button_size", "small", "big") + e.winner = "small" expect { - a = ab_test('button_size', 'small', 'big') - expect(a).to eq('small') + a = ab_test("button_size", "small", "big") + expect(a).to eq("small") }.not_to change { e.participant_count } end - it 'should not increment the counter for an not started experiment' do + it "should not increment the counter for an not started experiment" do expect(Split.configuration).to receive(:start_manually).and_return(true) - e = Split::ExperimentCatalog.find_or_create('button_size', 'small', 'big') + e = Split::ExperimentCatalog.find_or_create("button_size", "small", "big") expect { - a = ab_test('button_size', 'small', 'big') - expect(a).to eq('small') + a = ab_test("button_size", "small", "big") + expect(a).to eq("small") }.not_to change { e.participant_count } end it "should return the given alternative for an existing user" do - expect(ab_test('link_color', 'blue', 'red')).to eq ab_test('link_color', 'blue', 'red') + expect(ab_test("link_color", "blue", "red")).to eq ab_test("link_color", "blue", "red") end - it 'should always return the winner if one is present' do + it "should always return the winner if one is present" do experiment.winner = "orange" - expect(ab_test('link_color', 'blue', 'red')).to eq('orange') + expect(ab_test("link_color", "blue", "red")).to eq("orange") end it "should allow the alternative to be forced by passing it in the params" do # ?ab_test[link_color]=blue - @params = { 'ab_test' => { 'link_color' => 'blue' } } + @params = { "ab_test" => { "link_color" => "blue" } } - alternative = ab_test('link_color', 'blue', 'red') - expect(alternative).to eq('blue') + alternative = ab_test("link_color", "blue", "red") + expect(alternative).to eq("blue") - alternative = ab_test('link_color', { 'blue' => 1 }, 'red' => 5) - expect(alternative).to eq('blue') + alternative = ab_test("link_color", { "blue" => 1 }, "red" => 5) + expect(alternative).to eq("blue") - @params = { 'ab_test' => { 'link_color' => 'red' } } + @params = { "ab_test" => { "link_color" => "red" } } - alternative = ab_test('link_color', 'blue', 'red') - expect(alternative).to eq('red') + alternative = ab_test("link_color", "blue", "red") + expect(alternative).to eq("red") - alternative = ab_test('link_color', { 'blue' => 5 }, 'red' => 1) - expect(alternative).to eq('red') + alternative = ab_test("link_color", { "blue" => 5 }, "red" => 1) + expect(alternative).to eq("red") end it "should not allow an arbitrary alternative" do - @params = { 'ab_test' => { 'link_color' => 'pink' } } - alternative = ab_test('link_color', 'blue') - expect(alternative).to eq('blue') + @params = { "ab_test" => { "link_color" => "pink" } } + alternative = ab_test("link_color", "blue") + expect(alternative).to eq("blue") end it "should not store the split when a param forced alternative" do - @params = { 'ab_test' => { 'link_color' => 'blue' } } + @params = { "ab_test" => { "link_color" => "blue" } } expect(ab_user).not_to receive(:[]=) - ab_test('link_color', 'blue', 'red') + ab_test("link_color", "blue", "red") end it "SPLIT_DISABLE query parameter should also force the alternative (uses control)" do - @params = { 'SPLIT_DISABLE' => 'true' } - alternative = ab_test('link_color', 'blue', 'red') - expect(alternative).to eq('blue') - alternative = ab_test('link_color', { 'blue' => 1 }, 'red' => 5) - expect(alternative).to eq('blue') - alternative = ab_test('link_color', 'red', 'blue') - expect(alternative).to eq('red') - alternative = ab_test('link_color', { 'red' => 5 }, 'blue' => 1) - expect(alternative).to eq('red') + @params = { "SPLIT_DISABLE" => "true" } + alternative = ab_test("link_color", "blue", "red") + expect(alternative).to eq("blue") + alternative = ab_test("link_color", { "blue" => 1 }, "red" => 5) + expect(alternative).to eq("blue") + alternative = ab_test("link_color", "red", "blue") + expect(alternative).to eq("red") + alternative = ab_test("link_color", { "red" => 5 }, "blue" => 1) + expect(alternative).to eq("red") end it "should not store the split when Split generically disabled" do - @params = { 'SPLIT_DISABLE' => 'true' } + @params = { "SPLIT_DISABLE" => "true" } expect(ab_user).not_to receive(:[]=) - ab_test('link_color', 'blue', 'red') + ab_test("link_color", "blue", "red") end context "when store_override is set" do before { Split.configuration.store_override = true } it "should store the forced alternative" do - @params = { 'ab_test' => { 'link_color' => 'blue' } } - expect(ab_user).to receive(:[]=).with('link_color', 'blue') - ab_test('link_color', 'blue', 'red') + @params = { "ab_test" => { "link_color" => "blue" } } + expect(ab_user).to receive(:[]=).with("link_color", "blue") + ab_test("link_color", "blue", "red") end end @@ -165,35 +165,35 @@ before { Split.configuration.on_trial_choose = :some_method } it "should call the method" do expect(self).to receive(:some_method) - ab_test('link_color', 'blue', 'red') + ab_test("link_color", "blue", "red") end end it "should allow passing a block" do - alt = ab_test('link_color', 'blue', 'red') - ret = ab_test('link_color', 'blue', 'red') { |alternative| "shared/#{alternative}" } + alt = ab_test("link_color", "blue", "red") + ret = ab_test("link_color", "blue", "red") { |alternative| "shared/#{alternative}" } expect(ret).to eq("shared/#{alt}") end it "should allow the share of visitors see an alternative to be specified" do - ab_test('link_color', { 'blue' => 0.8 }, { 'red' => 20 }) - expect(['red', 'blue']).to include(ab_user['link_color']) + ab_test("link_color", { "blue" => 0.8 }, { "red" => 20 }) + expect(["red", "blue"]).to include(ab_user["link_color"]) end it "should allow alternative weighting interface as a single hash" do - ab_test('link_color', { 'blue' => 0.01 }, 'red' => 0.2) - experiment = Split::ExperimentCatalog.find('link_color') - expect(experiment.alternatives.map(&:name)).to eq(['blue', 'red']) + ab_test("link_color", { "blue" => 0.01 }, "red" => 0.2) + experiment = Split::ExperimentCatalog.find("link_color") + expect(experiment.alternatives.map(&:name)).to eq(["blue", "red"]) expect(experiment.alternatives.collect { |a| a.weight }).to match_array([0.01, 0.2]) end it "should only let a user participate in one experiment at a time" do - link_color = ab_test('link_color', 'blue', 'red') - ab_test('button_size', 'small', 'big') - expect(ab_user['link_color']).to eq(link_color) - big = Split::Alternative.new('big', 'button_size') + link_color = ab_test("link_color", "blue", "red") + ab_test("button_size", "small", "big") + expect(ab_user["link_color"]).to eq(link_color) + big = Split::Alternative.new("big", "button_size") expect(big.participant_count).to eq(0) - small = Split::Alternative.new('small', 'button_size') + small = Split::Alternative.new("small", "button_size") expect(small.participant_count).to eq(0) end @@ -201,113 +201,113 @@ Split.configure do |config| config.allow_multiple_experiments = true end - link_color = ab_test('link_color', 'blue', 'red') - button_size = ab_test('button_size', 'small', 'big') - expect(ab_user['link_color']).to eq(link_color) - expect(ab_user['button_size']).to eq(button_size) - button_size_alt = Split::Alternative.new(button_size, 'button_size') + link_color = ab_test("link_color", "blue", "red") + button_size = ab_test("button_size", "small", "big") + expect(ab_user["link_color"]).to eq(link_color) + expect(ab_user["button_size"]).to eq(button_size) + button_size_alt = Split::Alternative.new(button_size, "button_size") expect(button_size_alt.participant_count).to eq(1) end context "with allow_multiple_experiments = 'control'" do it "should let a user participate in many experiment with one non-'control' alternative" do Split.configure do |config| - config.allow_multiple_experiments = 'control' + config.allow_multiple_experiments = "control" end groups = 100.times.map do |n| - ab_test("test#{n}".to_sym, { 'control' => (100 - n) }, { "test#{n}-alt" => n }) + ab_test("test#{n}".to_sym, { "control" => (100 - n) }, { "test#{n}-alt" => n }) end experiments = ab_user.active_experiments expect(experiments.size).to be > 1 - count_control = experiments.values.count { |g| g == 'control' } + count_control = experiments.values.count { |g| g == "control" } expect(count_control).to eq(experiments.size - 1) - count_alts = groups.count { |g| g != 'control' } + count_alts = groups.count { |g| g != "control" } expect(count_alts).to eq(1) end context "when user already has experiment" do - let(:mock_user) { Split::User.new(self, { 'test_0' => 'test-alt' }) } + let(:mock_user) { Split::User.new(self, { "test_0" => "test-alt" }) } before do Split.configure do |config| - config.allow_multiple_experiments = 'control' + config.allow_multiple_experiments = "control" end - Split::ExperimentCatalog.find_or_initialize('test_0', 'control', 'test-alt').save - Split::ExperimentCatalog.find_or_initialize('test_1', 'control', 'test-alt').save + Split::ExperimentCatalog.find_or_initialize("test_0", "control", "test-alt").save + Split::ExperimentCatalog.find_or_initialize("test_1", "control", "test-alt").save end it "should restore previously selected alternative" do expect(ab_user.active_experiments.size).to eq 1 - expect(ab_test(:test_0, { 'control' => 100 }, { "test-alt" => 1 })).to eq 'test-alt' - expect(ab_test(:test_0, { 'control' => 1 }, { "test-alt" => 100 })).to eq 'test-alt' + expect(ab_test(:test_0, { "control" => 100 }, { "test-alt" => 1 })).to eq "test-alt" + expect(ab_test(:test_0, { "control" => 1 }, { "test-alt" => 100 })).to eq "test-alt" end it "should select the correct alternatives after experiment resets" do experiment = Split::ExperimentCatalog.find(:test_0) experiment.reset - mock_user[experiment.key] = 'test-alt' + mock_user[experiment.key] = "test-alt" expect(ab_user.active_experiments.size).to eq 1 - expect(ab_test(:test_0, { 'control' => 100 }, { "test-alt" => 1 })).to eq 'test-alt' - expect(ab_test(:test_0, { 'control' => 0 }, { "test-alt" => 100 })).to eq 'test-alt' + expect(ab_test(:test_0, { "control" => 100 }, { "test-alt" => 1 })).to eq "test-alt" + expect(ab_test(:test_0, { "control" => 0 }, { "test-alt" => 100 })).to eq "test-alt" end it "lets override existing choice" do pending "this requires user store reset on first call not depending on whelther it is current trial" - @params = { 'ab_test' => { 'test_1' => 'test-alt' } } + @params = { "ab_test" => { "test_1" => "test-alt" } } - expect(ab_test(:test_0, { 'control' => 0 }, { "test-alt" => 100 })).to eq 'control' - expect(ab_test(:test_1, { 'control' => 100 }, { "test-alt" => 1 })).to eq 'test-alt' + expect(ab_test(:test_0, { "control" => 0 }, { "test-alt" => 100 })).to eq "control" + expect(ab_test(:test_1, { "control" => 100 }, { "test-alt" => 1 })).to eq "test-alt" end end end it "should not over-write a finished key when an experiment is on a later version" do experiment.increment_version - ab_user = { experiment.key => 'blue', experiment.finished_key => true } + ab_user = { experiment.key => "blue", experiment.finished_key => true } finished_session = ab_user.dup - ab_test('link_color', 'blue', 'red') + ab_test("link_color", "blue", "red") expect(ab_user).to eq(finished_session) end end - describe 'metadata' do - context 'is defined' do + describe "metadata" do + context "is defined" do before do Split.configuration.experiments = { my_experiment: { alternatives: ["one", "two"], resettable: false, - metadata: { 'one' => 'Meta1', 'two' => 'Meta2' } + metadata: { "one" => "Meta1", "two" => "Meta2" } } } end - it 'should be passed to helper block' do - @params = { 'ab_test' => { 'my_experiment' => 'two' } } - expect(ab_test('my_experiment')).to eq 'two' - expect(ab_test('my_experiment') do |alternative, meta| + it "should be passed to helper block" do + @params = { "ab_test" => { "my_experiment" => "two" } } + expect(ab_test("my_experiment")).to eq "two" + expect(ab_test("my_experiment") do |alternative, meta| meta - end).to eq('Meta2') + end).to eq("Meta2") end - it 'should pass control metadata helper block if library disabled' do + it "should pass control metadata helper block if library disabled" do Split.configure do |config| config.enabled = false end - expect(ab_test('my_experiment')).to eq 'one' - expect(ab_test('my_experiment') do |_, meta| + expect(ab_test("my_experiment")).to eq "one" + expect(ab_test("my_experiment") do |_, meta| meta - end).to eq('Meta1') + end).to eq("Meta1") end end - context 'is not defined' do + context "is not defined" do before do Split.configuration.experiments = { my_experiment: { @@ -318,35 +318,35 @@ } end - it 'should be passed to helper block' do - expect(ab_test('my_experiment') do |alternative, meta| + it "should be passed to helper block" do + expect(ab_test("my_experiment") do |alternative, meta| meta end).to eq({}) end - it 'should pass control metadata helper block if library disabled' do + it "should pass control metadata helper block if library disabled" do Split.configure do |config| config.enabled = false end - expect(ab_test('my_experiment') do |_, meta| + expect(ab_test("my_experiment") do |_, meta| meta end).to eq({}) end end end - describe 'ab_finished' do - context 'for an experiment that the user participates in' do + describe "ab_finished" do + context "for an experiment that the user participates in" do before(:each) do - @experiment_name = 'link_color' - @alternatives = ['blue', 'red'] + @experiment_name = "link_color" + @alternatives = ["blue", "red"] @experiment = Split::ExperimentCatalog.find_or_create(@experiment_name, *@alternatives) @alternative_name = ab_test(@experiment_name, *@alternatives) @previous_completion_count = Split::Alternative.new(@alternative_name, @experiment_name).completed_count end - it 'should increment the counter for the completed alternative' do + it "should increment the counter for the completed alternative" do ab_finished(@experiment_name) new_completion_count = Split::Alternative.new(@alternative_name, @experiment_name).completed_count expect(new_completion_count).to eq(@previous_completion_count + 1) @@ -358,20 +358,20 @@ expect(ab_user[@experiment.finished_key]).to eq(true) end - it 'should not increment the counter if reset is false and the experiment has been already finished' do + it "should not increment the counter if reset is false and the experiment has been already finished" do 2.times { ab_finished(@experiment_name, { reset: false }) } new_completion_count = Split::Alternative.new(@alternative_name, @experiment_name).completed_count expect(new_completion_count).to eq(@previous_completion_count + 1) end - it 'should not increment the counter for an ended experiment' do - e = Split::ExperimentCatalog.find_or_create('button_size', 'small', 'big') - e.winner = 'small' - a = ab_test('button_size', 'small', 'big') - expect(a).to eq('small') + it "should not increment the counter for an ended experiment" do + e = Split::ExperimentCatalog.find_or_create("button_size", "small", "big") + e.winner = "small" + a = ab_test("button_size", "small", "big") + expect(a).to eq("small") expect { - ab_finished('button_size') - }.not_to change { Split::Alternative.new(a, 'button_size').completed_count } + ab_finished("button_size") + }.not_to change { Split::Alternative.new(a, "button_size").completed_count } end it "should clear out the user's participation from their session" do @@ -417,46 +417,46 @@ end end - context 'for an experiment that the user is excluded from' do + context "for an experiment that the user is excluded from" do before do - alternative = ab_test('link_color', 'blue', 'red') - expect(Split::Alternative.new(alternative, 'link_color').participant_count).to eq(1) - alternative = ab_test('button_size', 'small', 'big') - expect(Split::Alternative.new(alternative, 'button_size').participant_count).to eq(0) + alternative = ab_test("link_color", "blue", "red") + expect(Split::Alternative.new(alternative, "link_color").participant_count).to eq(1) + alternative = ab_test("button_size", "small", "big") + expect(Split::Alternative.new(alternative, "button_size").participant_count).to eq(0) end - it 'should not increment the completed counter' do + it "should not increment the completed counter" do # So, user should be participating in the link_color experiment and # receive the control for button_size. As the user is not participating in # the button size experiment, finishing it should not increase the # completion count for that alternative. expect { - ab_finished('button_size') - }.not_to change { Split::Alternative.new('small', 'button_size').completed_count } + ab_finished("button_size") + }.not_to change { Split::Alternative.new("small", "button_size").completed_count } end end - context 'for an experiment that the user does not participate in' do + context "for an experiment that the user does not participate in" do before do - Split::ExperimentCatalog.find_or_create(:not_started_experiment, 'control', 'alt') + Split::ExperimentCatalog.find_or_create(:not_started_experiment, "control", "alt") end - it 'should not raise an exception' do + it "should not raise an exception" do expect { ab_finished(:not_started_experiment) }.not_to raise_exception end - it 'should not change the user state when reset is false' do + it "should not change the user state when reset is false" do expect { ab_finished(:not_started_experiment, reset: false) }.not_to change { ab_user.keys }.from([]) end - it 'should not change the user state when reset is true' do + it "should not change the user state when reset is true" do expect(self).not_to receive(:reset!) ab_finished(:not_started_experiment) end - it 'should not increment the completed counter' do + it "should not increment the completed counter" do ab_finished(:not_started_experiment) - expect(Split::Alternative.new('control', :not_started_experiment).completed_count).to eq(0) - expect(Split::Alternative.new('alt', :not_started_experiment).completed_count).to eq(0) + expect(Split::Alternative.new("control", :not_started_experiment).completed_count).to eq(0) + expect(Split::Alternative.new("alt", :not_started_experiment).completed_count).to eq(0) end end end @@ -486,7 +486,7 @@ def should_finish_experiment(experiment_name, should_finish = true) alts = Split.configuration.experiments[experiment_name][:alternatives] experiment = Split::ExperimentCatalog.find_or_create(experiment_name, *alts) alt_name = ab_user[experiment.key] = alts.first - alt = double('alternative') + alt = double("alternative") expect(alt).to receive(:name).at_most(1).times.and_return(alt_name) expect(Split::Alternative).to receive(:new).at_most(1).times.with(alt_name, experiment_name.to_s).and_return(alt) if should_finish @@ -558,125 +558,125 @@ def should_finish_experiment(experiment_name, should_finish = true) end end - describe 'conversions' do - it 'should return a conversion rate for an alternative' do - alternative_name = ab_test('link_color', 'blue', 'red') + describe "conversions" do + it "should return a conversion rate for an alternative" do + alternative_name = ab_test("link_color", "blue", "red") - previous_convertion_rate = Split::Alternative.new(alternative_name, 'link_color').conversion_rate + previous_convertion_rate = Split::Alternative.new(alternative_name, "link_color").conversion_rate expect(previous_convertion_rate).to eq(0.0) - ab_finished('link_color') + ab_finished("link_color") - new_convertion_rate = Split::Alternative.new(alternative_name, 'link_color').conversion_rate + new_convertion_rate = Split::Alternative.new(alternative_name, "link_color").conversion_rate expect(new_convertion_rate).to eq(1.0) end end - describe 'active experiments' do - it 'should show an active test' do - alternative = ab_test('def', '4', '5', '6') + describe "active experiments" do + it "should show an active test" do + alternative = ab_test("def", "4", "5", "6") expect(active_experiments.count).to eq 1 expect(active_experiments.first[0]).to eq "def" expect(active_experiments.first[1]).to eq alternative end - it 'should show a finished test' do - alternative = ab_test('def', '4', '5', '6') - ab_finished('def', { reset: false }) + it "should show a finished test" do + alternative = ab_test("def", "4", "5", "6") + ab_finished("def", { reset: false }) expect(active_experiments.count).to eq 1 expect(active_experiments.first[0]).to eq "def" expect(active_experiments.first[1]).to eq alternative end - it 'should show an active test when an experiment is on a later version' do + it "should show an active test when an experiment is on a later version" do experiment.reset expect(experiment.version).to eq(1) - ab_test('link_color', 'blue', 'red') + ab_test("link_color", "blue", "red") expect(active_experiments.count).to eq 1 expect(active_experiments.first[0]).to eq "link_color" end - it 'should show versioned tests properly' do + it "should show versioned tests properly" do 10.times { experiment.reset } - alternative = ab_test(experiment.name, 'blue', 'red') + alternative = ab_test(experiment.name, "blue", "red") ab_finished(experiment.name, reset: false) expect(experiment.version).to eq(10) expect(active_experiments.count).to eq 1 - expect(active_experiments).to eq({ 'link_color' => alternative }) + expect(active_experiments).to eq({ "link_color" => alternative }) end - it 'should show multiple tests' do + it "should show multiple tests" do Split.configure do |config| config.allow_multiple_experiments = true end - alternative = ab_test('def', '4', '5', '6') - another_alternative = ab_test('ghi', '7', '8', '9') + alternative = ab_test("def", "4", "5", "6") + another_alternative = ab_test("ghi", "7", "8", "9") expect(active_experiments.count).to eq 2 - expect(active_experiments['def']).to eq alternative - expect(active_experiments['ghi']).to eq another_alternative + expect(active_experiments["def"]).to eq alternative + expect(active_experiments["ghi"]).to eq another_alternative end - it 'should not show tests with winners' do + it "should not show tests with winners" do Split.configure do |config| config.allow_multiple_experiments = true end - e = Split::ExperimentCatalog.find_or_create('def', '4', '5', '6') - e.winner = '4' - ab_test('def', '4', '5', '6') - another_alternative = ab_test('ghi', '7', '8', '9') + e = Split::ExperimentCatalog.find_or_create("def", "4", "5", "6") + e.winner = "4" + ab_test("def", "4", "5", "6") + another_alternative = ab_test("ghi", "7", "8", "9") expect(active_experiments.count).to eq 1 expect(active_experiments.first[0]).to eq "ghi" expect(active_experiments.first[1]).to eq another_alternative end end - describe 'when user is a robot' do + describe "when user is a robot" do before(:each) do - @request = OpenStruct.new(user_agent: 'Googlebot/2.1 (+http://www.google.com/bot.html)') + @request = OpenStruct.new(user_agent: "Googlebot/2.1 (+http://www.google.com/bot.html)") end - describe 'ab_test' do - it 'should return the control' do - alternative = ab_test('link_color', 'blue', 'red') + describe "ab_test" do + it "should return the control" do + alternative = ab_test("link_color", "blue", "red") expect(alternative).to eq experiment.control.name end - it 'should not create a experiment' do - ab_test('link_color', 'blue', 'red') - expect(Split::Experiment.new('link_color')).to be_a_new_record + it "should not create a experiment" do + ab_test("link_color", "blue", "red") + expect(Split::Experiment.new("link_color")).to be_a_new_record end it "should not increment the participation count" do - previous_red_count = Split::Alternative.new('red', 'link_color').participant_count - previous_blue_count = Split::Alternative.new('blue', 'link_color').participant_count + previous_red_count = Split::Alternative.new("red", "link_color").participant_count + previous_blue_count = Split::Alternative.new("blue", "link_color").participant_count - ab_test('link_color', 'blue', 'red') + ab_test("link_color", "blue", "red") - new_red_count = Split::Alternative.new('red', 'link_color').participant_count - new_blue_count = Split::Alternative.new('blue', 'link_color').participant_count + new_red_count = Split::Alternative.new("red", "link_color").participant_count + new_blue_count = Split::Alternative.new("blue", "link_color").participant_count expect((new_red_count + new_blue_count)).to eq(previous_red_count + previous_blue_count) end end - describe 'finished' do + describe "finished" do it "should not increment the completed count" do - alternative_name = ab_test('link_color', 'blue', 'red') + alternative_name = ab_test("link_color", "blue", "red") - previous_completion_count = Split::Alternative.new(alternative_name, 'link_color').completed_count + previous_completion_count = Split::Alternative.new(alternative_name, "link_color").completed_count - ab_finished('link_color') + ab_finished("link_color") - new_completion_count = Split::Alternative.new(alternative_name, 'link_color').completed_count + new_completion_count = Split::Alternative.new(alternative_name, "link_color").completed_count expect(new_completion_count).to eq(previous_completion_count) end end end - describe 'when providing custom ignore logic' do + describe "when providing custom ignore logic" do context "using a proc to configure custom logic" do before(:each) do Split.configure do |c| @@ -685,56 +685,56 @@ def should_finish_experiment(experiment_name, should_finish = true) end it "ignores the ab_test" do - ab_test('link_color', 'blue', 'red') + ab_test("link_color", "blue", "red") - red_count = Split::Alternative.new('red', 'link_color').participant_count - blue_count = Split::Alternative.new('blue', 'link_color').participant_count + red_count = Split::Alternative.new("red", "link_color").participant_count + blue_count = Split::Alternative.new("blue", "link_color").participant_count expect((red_count + blue_count)).to be(0) end end end shared_examples_for "a disabled test" do - describe 'ab_test' do - it 'should return the control' do - alternative = ab_test('link_color', 'blue', 'red') + describe "ab_test" do + it "should return the control" do + alternative = ab_test("link_color", "blue", "red") expect(alternative).to eq experiment.control.name end it "should not increment the participation count" do - previous_red_count = Split::Alternative.new('red', 'link_color').participant_count - previous_blue_count = Split::Alternative.new('blue', 'link_color').participant_count + previous_red_count = Split::Alternative.new("red", "link_color").participant_count + previous_blue_count = Split::Alternative.new("blue", "link_color").participant_count - ab_test('link_color', 'blue', 'red') + ab_test("link_color", "blue", "red") - new_red_count = Split::Alternative.new('red', 'link_color').participant_count - new_blue_count = Split::Alternative.new('blue', 'link_color').participant_count + new_red_count = Split::Alternative.new("red", "link_color").participant_count + new_blue_count = Split::Alternative.new("blue", "link_color").participant_count expect((new_red_count + new_blue_count)).to eq(previous_red_count + previous_blue_count) end end - describe 'finished' do + describe "finished" do it "should not increment the completed count" do - alternative_name = ab_test('link_color', 'blue', 'red') + alternative_name = ab_test("link_color", "blue", "red") - previous_completion_count = Split::Alternative.new(alternative_name, 'link_color').completed_count + previous_completion_count = Split::Alternative.new(alternative_name, "link_color").completed_count - ab_finished('link_color') + ab_finished("link_color") - new_completion_count = Split::Alternative.new(alternative_name, 'link_color').completed_count + new_completion_count = Split::Alternative.new(alternative_name, "link_color").completed_count expect(new_completion_count).to eq(previous_completion_count) end end end - describe 'when ip address is ignored' do + describe "when ip address is ignored" do context "individually" do before(:each) do - @request = OpenStruct.new(ip: '81.19.48.130') + @request = OpenStruct.new(ip: "81.19.48.130") Split.configure do |c| - c.ignore_ip_addresses << '81.19.48.130' + c.ignore_ip_addresses << "81.19.48.130" end end @@ -743,7 +743,7 @@ def should_finish_experiment(experiment_name, should_finish = true) context "for a range" do before(:each) do - @request = OpenStruct.new(ip: '81.19.48.129') + @request = OpenStruct.new(ip: "81.19.48.129") Split.configure do |c| c.ignore_ip_addresses << /81\.19\.48\.[0-9]+/ end @@ -754,9 +754,9 @@ def should_finish_experiment(experiment_name, should_finish = true) context "using both a range and a specific value" do before(:each) do - @request = OpenStruct.new(ip: '81.19.48.128') + @request = OpenStruct.new(ip: "81.19.48.128") Split.configure do |c| - c.ignore_ip_addresses << '81.19.48.130' + c.ignore_ip_addresses << "81.19.48.130" c.ignore_ip_addresses << /81\.19\.48\.[0-9]+/ end end @@ -766,119 +766,119 @@ def should_finish_experiment(experiment_name, should_finish = true) context "when ignored other address" do before do - @request = OpenStruct.new(ip: '1.1.1.1') + @request = OpenStruct.new(ip: "1.1.1.1") Split.configure do |c| - c.ignore_ip_addresses << '81.19.48.130' + c.ignore_ip_addresses << "81.19.48.130" end end it "works as usual" do - alternative_name = ab_test('link_color', 'red', 'blue') + alternative_name = ab_test("link_color", "red", "blue") expect { - ab_finished('link_color') - }.to change(Split::Alternative.new(alternative_name, 'link_color'), :completed_count).by(1) + ab_finished("link_color") + }.to change(Split::Alternative.new(alternative_name, "link_color"), :completed_count).by(1) end end end - describe 'when user is previewing' do + describe "when user is previewing" do before(:each) do - @request = OpenStruct.new(headers: { 'x-purpose' => 'preview' }) + @request = OpenStruct.new(headers: { "x-purpose" => "preview" }) end it_behaves_like "a disabled test" end - describe 'versioned experiments' do + describe "versioned experiments" do it "should use version zero if no version is present" do - alternative_name = ab_test('link_color', 'blue', 'red') + alternative_name = ab_test("link_color", "blue", "red") expect(experiment.version).to eq(0) - expect(ab_user['link_color']).to eq(alternative_name) + expect(ab_user["link_color"]).to eq(alternative_name) end it "should save the version of the experiment to the session" do experiment.reset expect(experiment.version).to eq(1) - alternative_name = ab_test('link_color', 'blue', 'red') - expect(ab_user['link_color:1']).to eq(alternative_name) + alternative_name = ab_test("link_color", "blue", "red") + expect(ab_user["link_color:1"]).to eq(alternative_name) end it "should load the experiment even if the version is not 0" do experiment.reset expect(experiment.version).to eq(1) - alternative_name = ab_test('link_color', 'blue', 'red') - expect(ab_user['link_color:1']).to eq(alternative_name) - return_alternative_name = ab_test('link_color', 'blue', 'red') + alternative_name = ab_test("link_color", "blue", "red") + expect(ab_user["link_color:1"]).to eq(alternative_name) + return_alternative_name = ab_test("link_color", "blue", "red") expect(return_alternative_name).to eq(alternative_name) end it "should reset the session of a user on an older version of the experiment" do - alternative_name = ab_test('link_color', 'blue', 'red') - expect(ab_user['link_color']).to eq(alternative_name) - alternative = Split::Alternative.new(alternative_name, 'link_color') + alternative_name = ab_test("link_color", "blue", "red") + expect(ab_user["link_color"]).to eq(alternative_name) + alternative = Split::Alternative.new(alternative_name, "link_color") expect(alternative.participant_count).to eq(1) experiment.reset expect(experiment.version).to eq(1) - alternative = Split::Alternative.new(alternative_name, 'link_color') + alternative = Split::Alternative.new(alternative_name, "link_color") expect(alternative.participant_count).to eq(0) - new_alternative_name = ab_test('link_color', 'blue', 'red') - expect(ab_user['link_color:1']).to eq(new_alternative_name) - new_alternative = Split::Alternative.new(new_alternative_name, 'link_color') + new_alternative_name = ab_test("link_color", "blue", "red") + expect(ab_user["link_color:1"]).to eq(new_alternative_name) + new_alternative = Split::Alternative.new(new_alternative_name, "link_color") expect(new_alternative.participant_count).to eq(1) end it "should cleanup old versions of experiments from the session" do - alternative_name = ab_test('link_color', 'blue', 'red') - expect(ab_user['link_color']).to eq(alternative_name) - alternative = Split::Alternative.new(alternative_name, 'link_color') + alternative_name = ab_test("link_color", "blue", "red") + expect(ab_user["link_color"]).to eq(alternative_name) + alternative = Split::Alternative.new(alternative_name, "link_color") expect(alternative.participant_count).to eq(1) experiment.reset expect(experiment.version).to eq(1) - alternative = Split::Alternative.new(alternative_name, 'link_color') + alternative = Split::Alternative.new(alternative_name, "link_color") expect(alternative.participant_count).to eq(0) - new_alternative_name = ab_test('link_color', 'blue', 'red') - expect(ab_user['link_color:1']).to eq(new_alternative_name) + new_alternative_name = ab_test("link_color", "blue", "red") + expect(ab_user["link_color:1"]).to eq(new_alternative_name) end it "should only count completion of users on the current version" do - alternative_name = ab_test('link_color', 'blue', 'red') - expect(ab_user['link_color']).to eq(alternative_name) - Split::Alternative.new(alternative_name, 'link_color') + alternative_name = ab_test("link_color", "blue", "red") + expect(ab_user["link_color"]).to eq(alternative_name) + Split::Alternative.new(alternative_name, "link_color") experiment.reset expect(experiment.version).to eq(1) - ab_finished('link_color') - alternative = Split::Alternative.new(alternative_name, 'link_color') + ab_finished("link_color") + alternative = Split::Alternative.new(alternative_name, "link_color") expect(alternative.completed_count).to eq(0) end end - context 'when redis is not available' do + context "when redis is not available" do before(:each) do expect(Split).to receive(:redis).at_most(5).times.and_raise(Errno::ECONNREFUSED.new) end - context 'and db_failover config option is turned off' do + context "and db_failover config option is turned off" do before(:each) do Split.configure do |config| config.db_failover = false end end - describe 'ab_test' do - it 'should raise an exception' do - expect { ab_test('link_color', 'blue', 'red') }.to raise_error(Errno::ECONNREFUSED) + describe "ab_test" do + it "should raise an exception" do + expect { ab_test("link_color", "blue", "red") }.to raise_error(Errno::ECONNREFUSED) end end - describe 'finished' do - it 'should raise an exception' do - expect { ab_finished('link_color') }.to raise_error(Errno::ECONNREFUSED) + describe "finished" do + it "should raise an exception" do + expect { ab_finished("link_color") }.to raise_error(Errno::ECONNREFUSED) end end @@ -890,29 +890,29 @@ def should_finish_experiment(experiment_name, should_finish = true) end it "should not attempt to connect to redis" do - expect { ab_test('link_color', 'blue', 'red') }.not_to raise_error + expect { ab_test("link_color", "blue", "red") }.not_to raise_error end it "should return control variable" do - expect(ab_test('link_color', 'blue', 'red')).to eq('blue') - expect { ab_finished('link_color') }.not_to raise_error + expect(ab_test("link_color", "blue", "red")).to eq("blue") + expect { ab_finished("link_color") }.not_to raise_error end end end - context 'and db_failover config option is turned on' do + context "and db_failover config option is turned on" do before(:each) do Split.configure do |config| config.db_failover = true end end - describe 'ab_test' do - it 'should not raise an exception' do - expect { ab_test('link_color', 'blue', 'red') }.not_to raise_error + describe "ab_test" do + it "should not raise an exception" do + expect { ab_test("link_color", "blue", "red") }.not_to raise_error end - it 'should call db_failover_on_db_error proc with error as parameter' do + it "should call db_failover_on_db_error proc with error as parameter" do Split.configure do |config| config.db_failover_on_db_error = proc do |error| expect(error).to be_a(Errno::ECONNREFUSED) @@ -920,40 +920,40 @@ def should_finish_experiment(experiment_name, should_finish = true) end expect(Split.configuration.db_failover_on_db_error).to receive(:call).and_call_original - ab_test('link_color', 'blue', 'red') + ab_test("link_color", "blue", "red") end - it 'should always use first alternative' do - expect(ab_test('link_color', 'blue', 'red')).to eq('blue') - expect(ab_test('link_color', { 'blue' => 0.01 }, 'red' => 0.2)).to eq('blue') - expect(ab_test('link_color', { 'blue' => 0.8 }, { 'red' => 20 })).to eq('blue') - expect(ab_test('link_color', 'blue', 'red') do |alternative| + it "should always use first alternative" do + expect(ab_test("link_color", "blue", "red")).to eq("blue") + expect(ab_test("link_color", { "blue" => 0.01 }, "red" => 0.2)).to eq("blue") + expect(ab_test("link_color", { "blue" => 0.8 }, { "red" => 20 })).to eq("blue") + expect(ab_test("link_color", "blue", "red") do |alternative| "shared/#{alternative}" - end).to eq('shared/blue') + end).to eq("shared/blue") end - context 'and db_failover_allow_parameter_override config option is turned on' do + context "and db_failover_allow_parameter_override config option is turned on" do before(:each) do Split.configure do |config| config.db_failover_allow_parameter_override = true end end - context 'and given an override parameter' do - it 'should use given override instead of the first alternative' do - @params = { 'ab_test' => { 'link_color' => 'red' } } - expect(ab_test('link_color', 'blue', 'red')).to eq('red') - expect(ab_test('link_color', 'blue', 'red', 'green')).to eq('red') - expect(ab_test('link_color', { 'blue' => 0.01 }, 'red' => 0.2)).to eq('red') - expect(ab_test('link_color', { 'blue' => 0.8 }, { 'red' => 20 })).to eq('red') - expect(ab_test('link_color', 'blue', 'red') do |alternative| + context "and given an override parameter" do + it "should use given override instead of the first alternative" do + @params = { "ab_test" => { "link_color" => "red" } } + expect(ab_test("link_color", "blue", "red")).to eq("red") + expect(ab_test("link_color", "blue", "red", "green")).to eq("red") + expect(ab_test("link_color", { "blue" => 0.01 }, "red" => 0.2)).to eq("red") + expect(ab_test("link_color", { "blue" => 0.8 }, { "red" => 20 })).to eq("red") + expect(ab_test("link_color", "blue", "red") do |alternative| "shared/#{alternative}" - end).to eq('shared/red') + end).to eq("shared/red") end end end - context 'and preloaded config given' do + context "and preloaded config given" do before do Split.configuration.experiments[:link_color] = { alternatives: [ "blue", "red" ], @@ -966,12 +966,12 @@ def should_finish_experiment(experiment_name, should_finish = true) end end - describe 'finished' do - it 'should not raise an exception' do - expect { ab_finished('link_color') }.not_to raise_error + describe "finished" do + it "should not raise an exception" do + expect { ab_finished("link_color") }.not_to raise_error end - it 'should call db_failover_on_db_error proc with error as parameter' do + it "should call db_failover_on_db_error proc with error as parameter" do Split.configure do |config| config.db_failover_on_db_error = proc do |error| expect(error).to be_a(Errno::ECONNREFUSED) @@ -979,7 +979,7 @@ def should_finish_experiment(experiment_name, should_finish = true) end expect(Split.configuration.db_failover_on_db_error).to receive(:call).and_call_original - ab_finished('link_color') + ab_finished("link_color") end end end @@ -1047,7 +1047,7 @@ def should_finish_experiment(experiment_name, should_finish = true) } ab_test :my_experiment experiment = Split::Experiment.new(:my_experiment) - expect(experiment.alternatives.collect { |a| [a.name, a.weight] }).to eq([['control_opt', 0.67], ['second_opt', 0.1], ['third_opt', 0.23]]) + expect(experiment.alternatives.collect { |a| [a.name, a.weight] }).to eq([["control_opt", 0.67], ["second_opt", 0.1], ["third_opt", 0.23]]) end it "accepts probability on some alternatives" do @@ -1062,7 +1062,7 @@ def should_finish_experiment(experiment_name, should_finish = true) ab_test :my_experiment experiment = Split::Experiment.new(:my_experiment) names_and_weights = experiment.alternatives.collect { |a| [a.name, a.weight] } - expect(names_and_weights).to eq([['control_opt', 0.34], ['second_opt', 0.215], ['third_opt', 0.23], ['fourth_opt', 0.215]]) + expect(names_and_weights).to eq([["control_opt", 0.34], ["second_opt", 0.215], ["third_opt", 0.23], ["fourth_opt", 0.215]]) expect(names_and_weights.inject(0) { |sum, nw| sum + nw[1] }).to eq(1.0) end @@ -1077,7 +1077,7 @@ def should_finish_experiment(experiment_name, should_finish = true) ab_test :my_experiment experiment = Split::Experiment.new(:my_experiment) names_and_weights = experiment.alternatives.collect { |a| [a.name, a.weight] } - expect(names_and_weights).to eq([['control_opt', 0.18], ['second_opt', 0.18], ['third_opt', 0.64]]) + expect(names_and_weights).to eq([["control_opt", 0.18], ["second_opt", 0.18], ["third_opt", 0.64]]) expect(names_and_weights.inject(0) { |sum, nw| sum + nw[1] }).to eq(1.0) end @@ -1096,11 +1096,11 @@ def should_finish_experiment(experiment_name, should_finish = true) end end - it 'should handle multiple experiments correctly' do - experiment2 = Split::ExperimentCatalog.find_or_create('link_color2', 'blue', 'red') - ab_test('link_color', 'blue', 'red') - ab_test('link_color2', 'blue', 'red') - ab_finished('link_color2') + it "should handle multiple experiments correctly" do + experiment2 = Split::ExperimentCatalog.find_or_create("link_color2", "blue", "red") + ab_test("link_color", "blue", "red") + ab_test("link_color2", "blue", "red") + ab_finished("link_color2") experiment2.alternatives.each do |alt| expect(alt.unfinished_count).to eq(0) @@ -1109,8 +1109,8 @@ def should_finish_experiment(experiment_name, should_finish = true) context "with goals" do before do - @experiment = { 'link_color' => ["purchase", "refund"] } - @alternatives = ['blue', 'red'] + @experiment = { "link_color" => ["purchase", "refund"] } + @alternatives = ["blue", "red"] @experiment_name, @goals = normalize_metric(@experiment) @goal1 = @goals[0] @goal2 = @goals[1] @@ -1124,8 +1124,8 @@ def should_finish_experiment(experiment_name, should_finish = true) describe "ab_test" do it "should allow experiment goals interface as a single hash" do ab_test(@experiment, *@alternatives) - experiment = Split::ExperimentCatalog.find('link_color') - expect(experiment.goals).to eq(['purchase', "refund"]) + experiment = Split::ExperimentCatalog.find("link_color") + expect(experiment.goals).to eq(["purchase", "refund"]) end end diff --git a/spec/metric_spec.rb b/spec/metric_spec.rb index 92721142..c1bf63e4 100644 --- a/spec/metric_spec.rb +++ b/spec/metric_spec.rb @@ -1,31 +1,31 @@ # frozen_string_literal: true -require 'spec_helper' -require 'split/metric' +require "spec_helper" +require "split/metric" describe Split::Metric do - describe 'possible experiments' do + describe "possible experiments" do it "should load the experiment if there is one, but no metric" do - experiment = Split::ExperimentCatalog.find_or_create('color', 'red', 'blue') - expect(Split::Metric.possible_experiments('color')).to eq([experiment]) + experiment = Split::ExperimentCatalog.find_or_create("color", "red", "blue") + expect(Split::Metric.possible_experiments("color")).to eq([experiment]) end it "should load the experiments in a metric" do - experiment1 = Split::ExperimentCatalog.find_or_create('color', 'red', 'blue') - experiment2 = Split::ExperimentCatalog.find_or_create('size', 'big', 'small') + experiment1 = Split::ExperimentCatalog.find_or_create("color", "red", "blue") + experiment2 = Split::ExperimentCatalog.find_or_create("size", "big", "small") - metric = Split::Metric.new(name: 'purchase', experiments: [experiment1, experiment2]) + metric = Split::Metric.new(name: "purchase", experiments: [experiment1, experiment2]) metric.save - expect(Split::Metric.possible_experiments('purchase')).to include(experiment1, experiment2) + expect(Split::Metric.possible_experiments("purchase")).to include(experiment1, experiment2) end it "should load both the metric experiments and an experiment with the same name" do - experiment1 = Split::ExperimentCatalog.find_or_create('purchase', 'red', 'blue') - experiment2 = Split::ExperimentCatalog.find_or_create('size', 'big', 'small') + experiment1 = Split::ExperimentCatalog.find_or_create("purchase", "red", "blue") + experiment2 = Split::ExperimentCatalog.find_or_create("size", "big", "small") - metric = Split::Metric.new(name: 'purchase', experiments: [experiment2]) + metric = Split::Metric.new(name: "purchase", experiments: [experiment2]) metric.save - expect(Split::Metric.possible_experiments('purchase')).to include(experiment1, experiment2) + expect(Split::Metric.possible_experiments("purchase")).to include(experiment1, experiment2) end end end diff --git a/spec/persistence/cookie_adapter_spec.rb b/spec/persistence/cookie_adapter_spec.rb index c31ec838..e53dd88f 100644 --- a/spec/persistence/cookie_adapter_spec.rb +++ b/spec/persistence/cookie_adapter_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require "spec_helper" -require 'rack/test' +require "rack/test" describe Split::Persistence::CookieAdapter do subject { described_class.new(context) } @@ -57,7 +57,7 @@ end it "ensure other added cookies are not overriden" do - context.response.set_cookie 'dummy', 'wow' + context.response.set_cookie "dummy", "wow" subject["foo"] = "FOO" expect(context.response.headers["Set-Cookie"]).to include("dummy=wow") expect(context.response.headers["Set-Cookie"]).to include("split=") @@ -78,7 +78,7 @@ controller.send(:"request=", ActionDispatch::Request.new({})) end - response = ActionDispatch::Response.new(200, {}, '').tap do |res| + response = ActionDispatch::Response.new(200, {}, "").tap do |res| res.request = controller.request end @@ -101,7 +101,7 @@ expect(subject["foo"]).to eq("FOO") expect(subject["bar"]).to eq("BAR") cookie_jar = context.request.env["action_dispatch.cookies"] - expect(cookie_jar['split']).to eq("{\"foo\":\"FOO\",\"bar\":\"BAR\"}") + expect(cookie_jar["split"]).to eq('{"foo":"FOO","bar":"BAR"}') end end end diff --git a/spec/persistence/dual_adapter_spec.rb b/spec/persistence/dual_adapter_spec.rb index a1ed6461..a3621228 100644 --- a/spec/persistence/dual_adapter_spec.rb +++ b/spec/persistence/dual_adapter_spec.rb @@ -1,9 +1,9 @@ # frozen_string_literal: true -require 'spec_helper' +require "spec_helper" describe Split::Persistence::DualAdapter do - let(:context) { 'some context' } + let(:context) { "some context" } let(:logged_in_adapter_instance) { double } let(:logged_in_adapter) do @@ -14,8 +14,8 @@ Class.new.tap { |c| allow(c).to receive(:new) { logged_out_adapter_instance } } end - context 'when fallback_to_logged_out_adapter is false' do - context 'when logged in' do + context "when fallback_to_logged_out_adapter is false" do + context "when logged in" do subject do described_class.with_config( logged_in: lambda { |context| true }, @@ -25,32 +25,32 @@ ).new(context) end - it '#[]=' do - expect(logged_in_adapter_instance).to receive(:[]=).with('my_key', 'my_value') + it "#[]=" do + expect(logged_in_adapter_instance).to receive(:[]=).with("my_key", "my_value") expect_any_instance_of(logged_out_adapter).not_to receive(:[]=) - subject['my_key'] = 'my_value' + subject["my_key"] = "my_value" end - it '#[]' do - expect(logged_in_adapter_instance).to receive(:[]).with('my_key') { 'my_value' } + it "#[]" do + expect(logged_in_adapter_instance).to receive(:[]).with("my_key") { "my_value" } expect_any_instance_of(logged_out_adapter).not_to receive(:[]) - expect(subject['my_key']).to eq('my_value') + expect(subject["my_key"]).to eq("my_value") end - it '#delete' do - expect(logged_in_adapter_instance).to receive(:delete).with('my_key') { 'my_value' } + it "#delete" do + expect(logged_in_adapter_instance).to receive(:delete).with("my_key") { "my_value" } expect_any_instance_of(logged_out_adapter).not_to receive(:delete) - expect(subject.delete('my_key')).to eq('my_value') + expect(subject.delete("my_key")).to eq("my_value") end - it '#keys' do - expect(logged_in_adapter_instance).to receive(:keys) { ['my_value'] } + it "#keys" do + expect(logged_in_adapter_instance).to receive(:keys) { ["my_value"] } expect_any_instance_of(logged_out_adapter).not_to receive(:keys) - expect(subject.keys).to eq(['my_value']) + expect(subject.keys).to eq(["my_value"]) end end - context 'when logged out' do + context "when logged out" do subject do described_class.with_config( logged_in: lambda { |context| false }, @@ -60,34 +60,34 @@ ).new(context) end - it '#[]=' do + it "#[]=" do expect_any_instance_of(logged_in_adapter).not_to receive(:[]=) - expect(logged_out_adapter_instance).to receive(:[]=).with('my_key', 'my_value') - subject['my_key'] = 'my_value' + expect(logged_out_adapter_instance).to receive(:[]=).with("my_key", "my_value") + subject["my_key"] = "my_value" end - it '#[]' do + it "#[]" do expect_any_instance_of(logged_in_adapter).not_to receive(:[]) - expect(logged_out_adapter_instance).to receive(:[]).with('my_key') { 'my_value' } - expect(subject['my_key']).to eq('my_value') + expect(logged_out_adapter_instance).to receive(:[]).with("my_key") { "my_value" } + expect(subject["my_key"]).to eq("my_value") end - it '#delete' do + it "#delete" do expect_any_instance_of(logged_in_adapter).not_to receive(:delete) - expect(logged_out_adapter_instance).to receive(:delete).with('my_key') { 'my_value' } - expect(subject.delete('my_key')).to eq('my_value') + expect(logged_out_adapter_instance).to receive(:delete).with("my_key") { "my_value" } + expect(subject.delete("my_key")).to eq("my_value") end - it '#keys' do + it "#keys" do expect_any_instance_of(logged_in_adapter).not_to receive(:keys) - expect(logged_out_adapter_instance).to receive(:keys) { ['my_value', 'my_value2'] } - expect(subject.keys).to eq(['my_value', 'my_value2']) + expect(logged_out_adapter_instance).to receive(:keys) { ["my_value", "my_value2"] } + expect(subject.keys).to eq(["my_value", "my_value2"]) end end end - context 'when fallback_to_logged_out_adapter is true' do - context 'when logged in' do + context "when fallback_to_logged_out_adapter is true" do + context "when logged in" do subject do described_class.with_config( logged_in: lambda { |context| true }, @@ -97,33 +97,33 @@ ).new(context) end - it '#[]=' do - expect(logged_in_adapter_instance).to receive(:[]=).with('my_key', 'my_value') - expect(logged_out_adapter_instance).to receive(:[]=).with('my_key', 'my_value') - expect(logged_out_adapter_instance).to receive(:[]).with('my_key') { nil } - subject['my_key'] = 'my_value' + it "#[]=" do + expect(logged_in_adapter_instance).to receive(:[]=).with("my_key", "my_value") + expect(logged_out_adapter_instance).to receive(:[]=).with("my_key", "my_value") + expect(logged_out_adapter_instance).to receive(:[]).with("my_key") { nil } + subject["my_key"] = "my_value" end - it '#[]' do - expect(logged_in_adapter_instance).to receive(:[]).with('my_key') { 'my_value' } + it "#[]" do + expect(logged_in_adapter_instance).to receive(:[]).with("my_key") { "my_value" } expect_any_instance_of(logged_out_adapter).not_to receive(:[]) - expect(subject['my_key']).to eq('my_value') + expect(subject["my_key"]).to eq("my_value") end - it '#delete' do - expect(logged_in_adapter_instance).to receive(:delete).with('my_key') { 'my_value' } - expect(logged_out_adapter_instance).to receive(:delete).with('my_key') { 'my_value' } - expect(subject.delete('my_key')).to eq('my_value') + it "#delete" do + expect(logged_in_adapter_instance).to receive(:delete).with("my_key") { "my_value" } + expect(logged_out_adapter_instance).to receive(:delete).with("my_key") { "my_value" } + expect(subject.delete("my_key")).to eq("my_value") end - it '#keys' do - expect(logged_in_adapter_instance).to receive(:keys) { ['my_value'] } - expect(logged_out_adapter_instance).to receive(:keys) { ['my_value', 'my_value2'] } - expect(subject.keys).to eq(['my_value', 'my_value2']) + it "#keys" do + expect(logged_in_adapter_instance).to receive(:keys) { ["my_value"] } + expect(logged_out_adapter_instance).to receive(:keys) { ["my_value", "my_value2"] } + expect(subject.keys).to eq(["my_value", "my_value2"]) end end - context 'when logged out' do + context "when logged out" do subject do described_class.with_config( logged_in: lambda { |context| false }, @@ -133,38 +133,38 @@ ).new(context) end - it '#[]=' do + it "#[]=" do expect_any_instance_of(logged_in_adapter).not_to receive(:[]=) - expect(logged_out_adapter_instance).to receive(:[]=).with('my_key', 'my_value') - expect(logged_out_adapter_instance).to receive(:[]).with('my_key') { nil } - subject['my_key'] = 'my_value' + expect(logged_out_adapter_instance).to receive(:[]=).with("my_key", "my_value") + expect(logged_out_adapter_instance).to receive(:[]).with("my_key") { nil } + subject["my_key"] = "my_value" end - it '#[]' do + it "#[]" do expect_any_instance_of(logged_in_adapter).not_to receive(:[]) - expect(logged_out_adapter_instance).to receive(:[]).with('my_key') { 'my_value' } - expect(subject['my_key']).to eq('my_value') + expect(logged_out_adapter_instance).to receive(:[]).with("my_key") { "my_value" } + expect(subject["my_key"]).to eq("my_value") end - it '#delete' do - expect(logged_in_adapter_instance).to receive(:delete).with('my_key') { 'my_value' } - expect(logged_out_adapter_instance).to receive(:delete).with('my_key') { 'my_value' } - expect(subject.delete('my_key')).to eq('my_value') + it "#delete" do + expect(logged_in_adapter_instance).to receive(:delete).with("my_key") { "my_value" } + expect(logged_out_adapter_instance).to receive(:delete).with("my_key") { "my_value" } + expect(subject.delete("my_key")).to eq("my_value") end - it '#keys' do - expect(logged_in_adapter_instance).to receive(:keys) { ['my_value'] } - expect(logged_out_adapter_instance).to receive(:keys) { ['my_value', 'my_value2'] } - expect(subject.keys).to eq(['my_value', 'my_value2']) + it "#keys" do + expect(logged_in_adapter_instance).to receive(:keys) { ["my_value"] } + expect(logged_out_adapter_instance).to receive(:keys) { ["my_value", "my_value2"] } + expect(subject.keys).to eq(["my_value", "my_value2"]) end end end - describe 'when errors in config' do + describe "when errors in config" do before { described_class.config.clear } let(:some_proc) { -> { } } - it 'when no logged in adapter' do + it "when no logged in adapter" do expect { described_class.with_config( logged_in: some_proc, @@ -173,7 +173,7 @@ }.to raise_error(StandardError, /:logged_in_adapter/) end - it 'when no logged out adapter' do + it "when no logged out adapter" do expect { described_class.with_config( logged_in: some_proc, @@ -182,7 +182,7 @@ }.to raise_error(StandardError, /:logged_out_adapter/) end - it 'when no logged in detector' do + it "when no logged in detector" do expect { described_class.with_config( logged_in_adapter: logged_in_adapter, diff --git a/spec/persistence/redis_adapter_spec.rb b/spec/persistence/redis_adapter_spec.rb index 85d376d0..1a1eac11 100644 --- a/spec/persistence/redis_adapter_spec.rb +++ b/spec/persistence/redis_adapter_spec.rb @@ -3,65 +3,65 @@ require "spec_helper" describe Split::Persistence::RedisAdapter do - let(:context) { double(lookup: 'blah') } + let(:context) { double(lookup: "blah") } subject { Split::Persistence::RedisAdapter.new(context) } - describe '#redis_key' do + describe "#redis_key" do before { Split::Persistence::RedisAdapter.reset_config! } - context 'default' do - it 'should raise error with prompt to set lookup_by' do + context "default" do + it "should raise error with prompt to set lookup_by" do expect { Split::Persistence::RedisAdapter.new(context) }.to raise_error(RuntimeError) end end - context 'config with key' do + context "config with key" do before { Split::Persistence::RedisAdapter.reset_config! } - subject { Split::Persistence::RedisAdapter.new(context, 'manual') } + subject { Split::Persistence::RedisAdapter.new(context, "manual") } it 'should be "persistence:manual"' do - expect(subject.redis_key).to eq('persistence:manual') + expect(subject.redis_key).to eq("persistence:manual") end end context 'config with lookup_by = proc { "block" }' do - before { Split::Persistence::RedisAdapter.with_config(lookup_by: proc { 'block' }) } + before { Split::Persistence::RedisAdapter.with_config(lookup_by: proc { "block" }) } it 'should be "persistence:block"' do - expect(subject.redis_key).to eq('persistence:block') + expect(subject.redis_key).to eq("persistence:block") end end - context 'config with lookup_by = proc { |context| context.test }' do - before { Split::Persistence::RedisAdapter.with_config(lookup_by: proc { 'block' }) } - let(:context) { double(test: 'block') } + context "config with lookup_by = proc { |context| context.test }" do + before { Split::Persistence::RedisAdapter.with_config(lookup_by: proc { "block" }) } + let(:context) { double(test: "block") } it 'should be "persistence:block"' do - expect(subject.redis_key).to eq('persistence:block') + expect(subject.redis_key).to eq("persistence:block") end end context 'config with lookup_by = "method_name"' do - before { Split::Persistence::RedisAdapter.with_config(lookup_by: 'method_name') } - let(:context) { double(method_name: 'val') } + before { Split::Persistence::RedisAdapter.with_config(lookup_by: "method_name") } + let(:context) { double(method_name: "val") } it 'should be "persistence:bar"' do - expect(subject.redis_key).to eq('persistence:val') + expect(subject.redis_key).to eq("persistence:val") end end - context 'config with namespace and lookup_by' do - before { Split::Persistence::RedisAdapter.with_config(lookup_by: proc { 'frag' }, namespace: 'namer') } + context "config with namespace and lookup_by" do + before { Split::Persistence::RedisAdapter.with_config(lookup_by: proc { "frag" }, namespace: "namer") } it 'should be "namer"' do - expect(subject.redis_key).to eq('namer:frag') + expect(subject.redis_key).to eq("namer:frag") end end end - describe '#find' do - before { Split::Persistence::RedisAdapter.with_config(lookup_by: proc { 'frag' }, namespace: 'a_namespace') } + describe "#find" do + before { Split::Persistence::RedisAdapter.with_config(lookup_by: proc { "frag" }, namespace: "a_namespace") } it "should create and user from a given key" do adapter = Split::Persistence::RedisAdapter.find(2) @@ -69,8 +69,8 @@ end end - context 'functional tests' do - before { Split::Persistence::RedisAdapter.with_config(lookup_by: 'lookup') } + context "functional tests" do + before { Split::Persistence::RedisAdapter.with_config(lookup_by: "lookup") } describe "#[] and #[]=" do it "should set and return the value for given key" do diff --git a/spec/redis_interface_spec.rb b/spec/redis_interface_spec.rb index 2d2bd362..578578f8 100644 --- a/spec/redis_interface_spec.rb +++ b/spec/redis_interface_spec.rb @@ -1,44 +1,44 @@ # frozen_string_literal: true -require 'spec_helper' +require "spec_helper" describe Split::RedisInterface do - let(:list_name) { 'list_name' } - let(:set_name) { 'set_name' } + let(:list_name) { "list_name" } + let(:set_name) { "set_name" } let(:interface) { described_class.new } - describe '#persist_list' do + describe "#persist_list" do subject(:persist_list) do interface.persist_list(list_name, %w(a b c d)) end specify do expect(persist_list).to eq %w(a b c d) - expect(Split.redis.lindex(list_name, 0)).to eq 'a' - expect(Split.redis.lindex(list_name, 1)).to eq 'b' - expect(Split.redis.lindex(list_name, 2)).to eq 'c' - expect(Split.redis.lindex(list_name, 3)).to eq 'd' + expect(Split.redis.lindex(list_name, 0)).to eq "a" + expect(Split.redis.lindex(list_name, 1)).to eq "b" + expect(Split.redis.lindex(list_name, 2)).to eq "c" + expect(Split.redis.lindex(list_name, 3)).to eq "d" expect(Split.redis.llen(list_name)).to eq 4 end - context 'list is overwritten but not deleted' do + context "list is overwritten but not deleted" do specify do expect(persist_list).to eq %w(a b c d) - interface.persist_list(list_name, ['z']) - expect(Split.redis.lindex(list_name, 0)).to eq 'z' + interface.persist_list(list_name, ["z"]) + expect(Split.redis.lindex(list_name, 0)).to eq "z" expect(Split.redis.llen(list_name)).to eq 1 end end end - describe '#add_to_set' do + describe "#add_to_set" do subject(:add_to_set) do - interface.add_to_set(set_name, 'something') + interface.add_to_set(set_name, "something") end specify do add_to_set - expect(Split.redis.sismember(set_name, 'something')).to be true + expect(Split.redis.sismember(set_name, "something")).to be true end end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index bb47c76c..3e9859c9 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,18 +1,18 @@ # frozen_string_literal: true -ENV['RACK_ENV'] = "test" +ENV["RACK_ENV"] = "test" -require 'rubygems' -require 'bundler/setup' +require "rubygems" +require "bundler/setup" -require 'simplecov' +require "simplecov" SimpleCov.start -require 'split' -require 'ostruct' -require 'yaml' +require "split" +require "ostruct" +require "yaml" -Dir['./spec/support/*.rb'].each { |f| require f } +Dir["./spec/support/*.rb"].each { |f| require f } module GlobalSharedContext extend RSpec::SharedContext @@ -30,7 +30,7 @@ module GlobalSharedContext end RSpec.configure do |config| - config.order = 'random' + config.order = "random" config.include GlobalSharedContext config.raise_errors_for_deprecations! end @@ -43,11 +43,11 @@ def params @params ||= {} end -def request(ua = 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_6; de-de) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.4 Safari/533.20.27') +def request(ua = "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_6; de-de) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.4 Safari/533.20.27") @request ||= begin r = OpenStruct.new r.user_agent = ua - r.ip = '192.168.1.1' + r.ip = "192.168.1.1" r end end diff --git a/spec/split_spec.rb b/spec/split_spec.rb index 2dbd2502..a1361889 100644 --- a/spec/split_spec.rb +++ b/spec/split_spec.rb @@ -1,18 +1,18 @@ # frozen_string_literal: true -require 'spec_helper' +require "spec_helper" RSpec.describe Split do around(:each) do |ex| - old_env, old_redis = [ENV.delete('REDIS_URL'), Split.redis] + old_env, old_redis = [ENV.delete("REDIS_URL"), Split.redis] ex.run - ENV['REDIS_URL'] = old_env + ENV["REDIS_URL"] = old_env Split.redis = old_redis end - describe '#redis=' do - it 'accepts a url string' do - Split.redis = 'redis://localhost:6379' + describe "#redis=" do + it "accepts a url string" do + Split.redis = "redis://localhost:6379" expect(Split.redis).to be_a(Redis) client = Split.redis.connection @@ -20,8 +20,8 @@ expect(client[:port]).to eq(6379) end - it 'accepts an options hash' do - Split.redis = { host: 'localhost', port: 6379, db: 12 } + it "accepts an options hash" do + Split.redis = { host: "localhost", port: 6379, db: 12 } expect(Split.redis).to be_a(Redis) client = Split.redis.connection @@ -30,13 +30,13 @@ expect(client[:db]).to eq(12) end - it 'accepts a valid Redis instance' do + it "accepts a valid Redis instance" do other_redis = Redis.new(url: "redis://localhost:6379") Split.redis = other_redis expect(Split.redis).to eq(other_redis) end - it 'raises an ArgumentError when server cannot be determined' do + it "raises an ArgumentError when server cannot be determined" do expect { Split.redis = Object.new }.to raise_error(ArgumentError) end end diff --git a/spec/trial_spec.rb b/spec/trial_spec.rb index 3157ed21..24e55017 100644 --- a/spec/trial_spec.rb +++ b/spec/trial_spec.rb @@ -1,18 +1,18 @@ # frozen_string_literal: true -require 'spec_helper' -require 'split/trial' +require "spec_helper" +require "split/trial" describe Split::Trial do let(:user) { mock_user } - let(:alternatives) { ['basket', 'cart'] } + let(:alternatives) { ["basket", "cart"] } let(:experiment) do - Split::Experiment.new('basket_text', alternatives: alternatives).save + Split::Experiment.new("basket_text", alternatives: alternatives).save end it "should be initializeable" do - experiment = double('experiment') - alternative = double('alternative', kind_of?: Split::Alternative) + experiment = double("experiment") + alternative = double("alternative", kind_of?: Split::Alternative) trial = Split::Trial.new(experiment: experiment, alternative: alternative) expect(trial.experiment).to eq(experiment) expect(trial.alternative).to eq(alternative) @@ -20,35 +20,35 @@ describe "alternative" do it "should use the alternative if specified" do - alternative = double('alternative', kind_of?: Split::Alternative) - trial = Split::Trial.new(experiment: double('experiment'), + alternative = double("alternative", kind_of?: Split::Alternative) + trial = Split::Trial.new(experiment: double("experiment"), alternative: alternative, user: user) expect(trial).not_to receive(:choose) expect(trial.alternative).to eq(alternative) end it "should load the alternative when the alternative name is set" do - experiment = Split::Experiment.new('basket_text', alternatives: ['basket', 'cart']) + experiment = Split::Experiment.new("basket_text", alternatives: ["basket", "cart"]) experiment.save - trial = Split::Trial.new(experiment: experiment, alternative: 'basket') - expect(trial.alternative.name).to eq('basket') + trial = Split::Trial.new(experiment: experiment, alternative: "basket") + expect(trial.alternative.name).to eq("basket") end end describe "metadata" do let(:metadata) { Hash[alternatives.map { |k| [k, "Metadata for #{k}"] }] } let(:experiment) do - Split::Experiment.new('basket_text', alternatives: alternatives, metadata: metadata).save + Split::Experiment.new("basket_text", alternatives: alternatives, metadata: metadata).save end - it 'has metadata on each trial' do - trial = Split::Trial.new(experiment: experiment, user: user, metadata: metadata['cart'], - override: 'cart') - expect(trial.metadata).to eq(metadata['cart']) + it "has metadata on each trial" do + trial = Split::Trial.new(experiment: experiment, user: user, metadata: metadata["cart"], + override: "cart") + expect(trial.metadata).to eq(metadata["cart"]) end - it 'has metadata on each trial from the experiment' do + it "has metadata on each trial from the experiment" do trial = Split::Trial.new(experiment: experiment, user: user) trial.choose! expect(trial.metadata).to eq(metadata[trial.alternative.name]) @@ -57,24 +57,24 @@ end describe "#choose!" do - let(:context) { double(on_trial_callback: 'test callback') } + let(:context) { double(on_trial_callback: "test callback") } let(:trial) do Split::Trial.new(user: user, experiment: experiment) end - shared_examples_for 'a trial with callbacks' do - it 'does not run if on_trial callback is not respondable' do + shared_examples_for "a trial with callbacks" do + it "does not run if on_trial callback is not respondable" do Split.configuration.on_trial = :foo allow(context).to receive(:respond_to?).with(:foo, true).and_return false expect(context).to_not receive(:foo) trial.choose! context end - it 'runs on_trial callback' do + it "runs on_trial callback" do Split.configuration.on_trial = :on_trial_callback expect(context).to receive(:on_trial_callback) trial.choose! context end - it 'does not run nil on_trial callback' do + it "does not run nil on_trial callback" do Split.configuration.on_trial = nil expect(context).not_to receive(:on_trial_callback) trial.choose! context @@ -89,12 +89,12 @@ def expect_alternative(trial, alternative_name) end context "when override is present" do - let(:override) { 'cart' } + let(:override) { "cart" } let(:trial) do Split::Trial.new(user: user, experiment: experiment, override: override) end - it_behaves_like 'a trial with callbacks' + it_behaves_like "a trial with callbacks" it "picks the override" do expect(experiment).to_not receive(:next_alternative) @@ -103,7 +103,7 @@ def expect_alternative(trial, alternative_name) context "when alternative doesn't exist" do let(:override) { nil } - it 'falls back on next_alternative' do + it "falls back on next_alternative" do expect(experiment).to receive(:next_alternative).and_call_original expect_alternative(trial, alternatives) end @@ -121,7 +121,7 @@ def expect_alternative(trial, alternative_name) expect(context).not_to receive(:on_trial_callback) - expect_alternative(trial, 'basket') + expect_alternative(trial, "basket") Split.configuration.on_trial = nil end end @@ -133,7 +133,7 @@ def expect_alternative(trial, alternative_name) expect(experiment).to_not receive(:next_alternative) expect(context).not_to receive(:on_trial_callback) - expect_alternative(trial, 'basket') + expect_alternative(trial, "basket") Split.configuration.enabled = true Split.configuration.on_trial = nil @@ -145,13 +145,13 @@ def expect_alternative(trial, alternative_name) Split::Trial.new(user: user, experiment: experiment) end - it_behaves_like 'a trial with callbacks' + it_behaves_like "a trial with callbacks" it "picks the winner" do - experiment.winner = 'cart' + experiment.winner = "cart" expect(experiment).to_not receive(:next_alternative) - expect_alternative(trial, 'cart') + expect_alternative(trial, "cart") end end @@ -160,27 +160,27 @@ def expect_alternative(trial, alternative_name) Split::Trial.new(user: user, experiment: experiment, exclude: true) end - it_behaves_like 'a trial with callbacks' + it_behaves_like "a trial with callbacks" it "picks the control" do expect(experiment).to_not receive(:next_alternative) - expect_alternative(trial, 'basket') + expect_alternative(trial, "basket") end end context "when user is already participating" do - it_behaves_like 'a trial with callbacks' + it_behaves_like "a trial with callbacks" it "picks the same alternative" do - user[experiment.key] = 'basket' + user[experiment.key] = "basket" expect(experiment).to_not receive(:next_alternative) - expect_alternative(trial, 'basket') + expect_alternative(trial, "basket") end context "when alternative is not found" do it "falls back on next_alternative" do - user[experiment.key] = 'notfound' + user[experiment.key] = "notfound" expect(experiment).to receive(:next_alternative).and_call_original expect_alternative(trial, alternatives) end @@ -214,7 +214,7 @@ def expect_alternative(trial, alternative_name) expect(experiment).to_not receive(:next_alternative) expect(context).not_to receive(:on_trial_callback) - expect_alternative(trial, 'basket') + expect_alternative(trial, "basket") Split.configuration.enabled = true Split.configuration.on_trial = nil @@ -230,9 +230,9 @@ def expect_alternative(trial, alternative_name) end describe "#complete!" do - context 'when there are no goals' do + context "when there are no goals" do let(:trial) { Split::Trial.new(user: user, experiment: experiment) } - it 'should complete the trial' do + it "should complete the trial" do trial.choose! old_completed_count = trial.alternative.completed_count trial.complete! @@ -269,7 +269,7 @@ def expect_alternative(trial, alternative_name) context "when override is present" do it "stores when store_override is true" do - trial = Split::Trial.new(user: user, experiment: experiment, override: 'basket') + trial = Split::Trial.new(user: user, experiment: experiment, override: "basket") Split.configuration.store_override = true expect(user).to receive("[]=") @@ -278,7 +278,7 @@ def expect_alternative(trial, alternative_name) end it "does not store when store_override is false" do - trial = Split::Trial.new(user: user, experiment: experiment, override: 'basket') + trial = Split::Trial.new(user: user, experiment: experiment, override: "basket") expect(user).to_not receive("[]=") trial.choose! @@ -311,13 +311,13 @@ def expect_alternative(trial, alternative_name) end end - context 'when experiment has winner' do + context "when experiment has winner" do let(:trial) do - experiment.winner = 'cart' + experiment.winner = "cart" Split::Trial.new(user: user, experiment: experiment) end - it 'does not store' do + it "does not store" do expect(user).to_not receive("[]=") trial.choose! end diff --git a/spec/user_spec.rb b/spec/user_spec.rb index f121ffb6..acb51e55 100644 --- a/spec/user_spec.rb +++ b/spec/user_spec.rb @@ -1,73 +1,73 @@ # frozen_string_literal: true -require 'spec_helper' -require 'split/experiment_catalog' -require 'split/experiment' -require 'split/user' +require "spec_helper" +require "split/experiment_catalog" +require "split/experiment" +require "split/user" describe Split::User do - let(:user_keys) { { 'link_color' => 'blue' } } + let(:user_keys) { { "link_color" => "blue" } } let(:context) { double(session: { split: user_keys }) } - let(:experiment) { Split::Experiment.new('link_color') } + let(:experiment) { Split::Experiment.new("link_color") } before(:each) do @subject = described_class.new(context) end - it 'delegates methods correctly' do - expect(@subject['link_color']).to eq(@subject.user['link_color']) + it "delegates methods correctly" do + expect(@subject["link_color"]).to eq(@subject.user["link_color"]) end - context '#cleanup_old_versions!' do + context "#cleanup_old_versions!" do let(:experiment_version) { "#{experiment.name}:1" } let(:second_experiment_version) { "#{experiment.name}_another:1" } let(:third_experiment_version) { "variation_of_#{experiment.name}:1" } let(:user_keys) do { - experiment_version => 'blue', - second_experiment_version => 'red', - third_experiment_version => 'yellow' + experiment_version => "blue", + second_experiment_version => "red", + third_experiment_version => "yellow" } end before(:each) { @subject.cleanup_old_versions!(experiment) } - it 'removes key if old experiment is found' do + it "removes key if old experiment is found" do expect(@subject.keys).not_to include(experiment_version) end - it 'does not remove other keys' do + it "does not remove other keys" do expect(@subject.keys).to include(second_experiment_version, third_experiment_version) end end - context '#cleanup_old_experiments!' do - it 'removes key if experiment is not found' do + context "#cleanup_old_experiments!" do + it "removes key if experiment is not found" do @subject.cleanup_old_experiments! expect(@subject.keys).to be_empty end - it 'removes key if experiment has a winner' do - allow(Split::ExperimentCatalog).to receive(:find).with('link_color').and_return(experiment) + it "removes key if experiment has a winner" do + allow(Split::ExperimentCatalog).to receive(:find).with("link_color").and_return(experiment) allow(experiment).to receive(:start_time).and_return(Date.today) allow(experiment).to receive(:has_winner?).and_return(true) @subject.cleanup_old_experiments! expect(@subject.keys).to be_empty end - it 'removes key if experiment has not started yet' do - allow(Split::ExperimentCatalog).to receive(:find).with('link_color').and_return(experiment) + it "removes key if experiment has not started yet" do + allow(Split::ExperimentCatalog).to receive(:find).with("link_color").and_return(experiment) allow(experiment).to receive(:has_winner?).and_return(false) @subject.cleanup_old_experiments! expect(@subject.keys).to be_empty end - context 'with finished key' do - let(:user_keys) { { 'link_color' => 'blue', 'link_color:finished' => true } } + context "with finished key" do + let(:user_keys) { { "link_color" => "blue", "link_color:finished" => true } } - it 'does not remove finished key for experiment without a winner' do - allow(Split::ExperimentCatalog).to receive(:find).with('link_color').and_return(experiment) - allow(Split::ExperimentCatalog).to receive(:find).with('link_color:finished').and_return(nil) + it "does not remove finished key for experiment without a winner" do + allow(Split::ExperimentCatalog).to receive(:find).with("link_color").and_return(experiment) + allow(Split::ExperimentCatalog).to receive(:find).with("link_color:finished").and_return(nil) allow(experiment).to receive(:start_time).and_return(Date.today) allow(experiment).to receive(:has_winner?).and_return(false) @subject.cleanup_old_experiments! @@ -76,29 +76,29 @@ end end - context 'when already cleaned up' do + context "when already cleaned up" do before do @subject.cleanup_old_experiments! end - it 'does not clean up again' do + it "does not clean up again" do expect(@subject).to_not receive(:keys_without_finished) @subject.cleanup_old_experiments! end end end - context 'allows user to be loaded from adapter' do - it 'loads user from adapter (RedisAdapter)' do + context "allows user to be loaded from adapter" do + it "loads user from adapter (RedisAdapter)" do user = Split::Persistence::RedisAdapter.new(nil, 112233) - user['foo'] = 'bar' + user["foo"] = "bar" ab_user = Split::User.find(112233, :redis) - expect(ab_user['foo']).to eql('bar') + expect(ab_user["foo"]).to eql("bar") end - it 'returns nil if adapter does not implement a finder method' do + it "returns nil if adapter does not implement a finder method" do ab_user = Split::User.find(112233, :dual_adapter) expect(ab_user).to be_nil end diff --git a/split.gemspec b/split.gemspec index 626d5586..4c56739c 100644 --- a/split.gemspec +++ b/split.gemspec @@ -9,7 +9,7 @@ Gem::Specification.new do |s| s.version = Split::VERSION s.platform = Gem::Platform::RUBY s.authors = ["Andrew Nesbitt"] - s.licenses = ['MIT'] + s.licenses = ["MIT"] s.email = ["andrewnez@gmail.com"] s.homepage = "https://github.com/splitrb/split" s.summary = "Rack based split testing framework" @@ -23,22 +23,22 @@ Gem::Specification.new do |s| "mailing_list_uri" => "https://groups.google.com/d/forum/split-ruby" } - s.required_ruby_version = '>= 2.5.0' - s.required_rubygems_version = '>= 2.0.0' + s.required_ruby_version = ">= 2.5.0" + s.required_rubygems_version = ">= 2.0.0" s.files = `git ls-files`.split("\n") s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") s.require_paths = ["lib"] - s.add_dependency 'redis', '>= 4.2' - s.add_dependency 'sinatra', '>= 1.2.6' - s.add_dependency 'rubystats', '>= 0.3.0' + s.add_dependency "redis", ">= 4.2" + s.add_dependency "sinatra", ">= 1.2.6" + s.add_dependency "rubystats", ">= 0.3.0" - s.add_development_dependency 'bundler', '>= 1.17' - s.add_development_dependency 'simplecov', '~> 0.15' - s.add_development_dependency 'rack-test', '~> 1.1' - s.add_development_dependency 'rake', '~> 13' - s.add_development_dependency 'rspec', '~> 3.7' - s.add_development_dependency 'pry', '~> 0.10' - s.add_development_dependency 'rails', '>= 5.0' + s.add_development_dependency "bundler", ">= 1.17" + s.add_development_dependency "simplecov", "~> 0.15" + s.add_development_dependency "rack-test", "~> 1.1" + s.add_development_dependency "rake", "~> 13" + s.add_development_dependency "rspec", "~> 3.7" + s.add_development_dependency "pry", "~> 0.10" + s.add_development_dependency "rails", ">= 5.0" end