split 4.0.1 → 4.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +6 -3
  3. data/.rubocop.yml +2 -5
  4. data/CHANGELOG.md +23 -0
  5. data/CONTRIBUTING.md +1 -1
  6. data/Gemfile +2 -1
  7. data/README.md +4 -2
  8. data/Rakefile +4 -5
  9. data/gemfiles/5.2.gemfile +1 -3
  10. data/gemfiles/6.0.gemfile +1 -3
  11. data/gemfiles/6.1.gemfile +1 -3
  12. data/gemfiles/7.0.gemfile +2 -3
  13. data/lib/split/algorithms/block_randomization.rb +5 -6
  14. data/lib/split/algorithms/whiplash.rb +16 -18
  15. data/lib/split/algorithms.rb +22 -0
  16. data/lib/split/alternative.rb +21 -22
  17. data/lib/split/cache.rb +0 -1
  18. data/lib/split/combined_experiments_helper.rb +4 -4
  19. data/lib/split/configuration.rb +83 -84
  20. data/lib/split/dashboard/helpers.rb +6 -7
  21. data/lib/split/dashboard/pagination_helpers.rb +53 -54
  22. data/lib/split/dashboard/public/style.css +5 -2
  23. data/lib/split/dashboard/views/index.erb +19 -4
  24. data/lib/split/dashboard.rb +29 -23
  25. data/lib/split/encapsulated_helper.rb +4 -6
  26. data/lib/split/experiment.rb +84 -88
  27. data/lib/split/experiment_catalog.rb +6 -5
  28. data/lib/split/extensions/string.rb +1 -1
  29. data/lib/split/goals_collection.rb +8 -10
  30. data/lib/split/helper.rb +19 -19
  31. data/lib/split/metric.rb +4 -5
  32. data/lib/split/persistence/cookie_adapter.rb +44 -47
  33. data/lib/split/persistence/dual_adapter.rb +7 -8
  34. data/lib/split/persistence/redis_adapter.rb +2 -3
  35. data/lib/split/persistence/session_adapter.rb +0 -2
  36. data/lib/split/persistence.rb +4 -4
  37. data/lib/split/redis_interface.rb +1 -2
  38. data/lib/split/trial.rb +23 -24
  39. data/lib/split/user.rb +12 -13
  40. data/lib/split/version.rb +1 -1
  41. data/lib/split/zscore.rb +1 -3
  42. data/lib/split.rb +26 -25
  43. data/spec/algorithms/block_randomization_spec.rb +6 -5
  44. data/spec/algorithms/weighted_sample_spec.rb +6 -5
  45. data/spec/algorithms/whiplash_spec.rb +4 -5
  46. data/spec/alternative_spec.rb +35 -36
  47. data/spec/cache_spec.rb +15 -19
  48. data/spec/combined_experiments_helper_spec.rb +18 -17
  49. data/spec/configuration_spec.rb +32 -38
  50. data/spec/dashboard/pagination_helpers_spec.rb +69 -67
  51. data/spec/dashboard/paginator_spec.rb +10 -9
  52. data/spec/dashboard_helpers_spec.rb +19 -18
  53. data/spec/dashboard_spec.rb +67 -35
  54. data/spec/encapsulated_helper_spec.rb +12 -14
  55. data/spec/experiment_catalog_spec.rb +14 -13
  56. data/spec/experiment_spec.rb +121 -123
  57. data/spec/goals_collection_spec.rb +17 -15
  58. data/spec/helper_spec.rb +379 -382
  59. data/spec/metric_spec.rb +14 -14
  60. data/spec/persistence/cookie_adapter_spec.rb +23 -8
  61. data/spec/persistence/dual_adapter_spec.rb +71 -71
  62. data/spec/persistence/redis_adapter_spec.rb +25 -26
  63. data/spec/persistence/session_adapter_spec.rb +2 -3
  64. data/spec/persistence_spec.rb +1 -2
  65. data/spec/redis_interface_spec.rb +16 -14
  66. data/spec/spec_helper.rb +15 -13
  67. data/spec/split_spec.rb +11 -11
  68. data/spec/support/cookies_mock.rb +1 -2
  69. data/spec/trial_spec.rb +61 -60
  70. data/spec/user_spec.rb +36 -36
  71. data/split.gemspec +20 -20
  72. metadata +7 -10
  73. data/.rubocop_todo.yml +0 -226
  74. data/Appraisals +0 -19
  75. data/gemfiles/5.0.gemfile +0 -9
  76. data/gemfiles/5.1.gemfile +0 -9
@@ -1,15 +1,16 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Split
3
4
  class ExperimentCatalog
4
5
  # Return all experiments
5
6
  def self.all
6
7
  # Call compact to prevent nil experiments from being returned -- seems to happen during gem upgrades
7
- Split.redis.smembers(:experiments).map {|e| find(e)}.compact
8
+ Split.redis.smembers(:experiments).map { |e| find(e) }.compact
8
9
  end
9
10
 
10
11
  # Return experiments without a winner (considered "active") first
11
12
  def self.all_active_first
12
- all.partition{|e| not e.winner}.map{|es| es.sort_by(&:name)}.flatten
13
+ all.partition { |e| not e.winner }.map { |es| es.sort_by(&:name) }.flatten
13
14
  end
14
15
 
15
16
  def self.find(name)
@@ -19,14 +20,14 @@ module Split
19
20
  def self.find_or_initialize(metric_descriptor, control = nil, *alternatives)
20
21
  # Check if array is passed to ab_test
21
22
  # e.g. ab_test('name', ['Alt 1', 'Alt 2', 'Alt 3'])
22
- if control.is_a? Array and alternatives.length.zero?
23
+ if control.is_a?(Array) && alternatives.length.zero?
23
24
  control, alternatives = control.first, control[1..-1]
24
25
  end
25
26
 
26
27
  experiment_name_with_version, goals = normalize_experiment(metric_descriptor)
27
- experiment_name = experiment_name_with_version.to_s.split(':')[0]
28
+ experiment_name = experiment_name_with_version.to_s.split(":")[0]
28
29
  Split::Experiment.new(experiment_name,
29
- :alternatives => [control].compact + alternatives, :goals => goals)
30
+ alternatives: [control].compact + alternatives, goals: goals)
30
31
  end
31
32
 
32
33
  def self.find_or_create(metric_descriptor, control = nil, *alternatives)
@@ -4,7 +4,7 @@ class String
4
4
  # Constatntize is often provided by ActiveSupport, but ActiveSupport is not a dependency of Split.
5
5
  unless method_defined?(:constantize)
6
6
  def constantize
7
- names = self.split('::')
7
+ names = self.split("::")
8
8
  names.shift if names.empty? || names.first.empty?
9
9
 
10
10
  constant = Object
@@ -2,8 +2,7 @@
2
2
 
3
3
  module Split
4
4
  class GoalsCollection
5
-
6
- def initialize(experiment_name, goals=nil)
5
+ def initialize(experiment_name, goals = nil)
7
6
  @experiment_name = experiment_name
8
7
  @goals = goals
9
8
  end
@@ -15,10 +14,10 @@ module Split
15
14
  def load_from_configuration
16
15
  goals = Split.configuration.experiment_for(@experiment_name)[:goals]
17
16
 
18
- if goals.nil?
19
- goals = []
20
- else
17
+ if goals
21
18
  goals.flatten
19
+ else
20
+ []
22
21
  end
23
22
  end
24
23
 
@@ -29,7 +28,7 @@ module Split
29
28
 
30
29
  def validate!
31
30
  unless @goals.nil? || @goals.kind_of?(Array)
32
- raise ArgumentError, 'Goals must be an array'
31
+ raise ArgumentError, "Goals must be an array"
33
32
  end
34
33
  end
35
34
 
@@ -38,9 +37,8 @@ module Split
38
37
  end
39
38
 
40
39
  private
41
-
42
- def goals_key
43
- "#{@experiment_name}:goals"
44
- end
40
+ def goals_key
41
+ "#{@experiment_name}:goals"
42
+ end
45
43
  end
46
44
  end
data/lib/split/helper.rb CHANGED
@@ -12,9 +12,9 @@ module Split
12
12
  alternative = if Split.configuration.enabled && !exclude_visitor?
13
13
  experiment.save
14
14
  raise(Split::InvalidExperimentsFormatError) unless (Split.configuration.experiments || {}).fetch(experiment.name.to_sym, {})[:combined_experiments].nil?
15
- trial = Trial.new(:user => ab_user, :experiment => experiment,
16
- :override => override_alternative(experiment.name), :exclude => exclude_visitor?,
17
- :disabled => split_generically_disabled?)
15
+ trial = Trial.new(user: ab_user, experiment: experiment,
16
+ override: override_alternative(experiment.name), exclude: exclude_visitor?,
17
+ disabled: split_generically_disabled?)
18
18
  alt = trial.choose!(self)
19
19
  alt ? alt.name : nil
20
20
  else
@@ -44,21 +44,21 @@ module Split
44
44
  ab_user.delete(experiment.key)
45
45
  end
46
46
 
47
- def finish_experiment(experiment, options = {:reset => true})
47
+ def finish_experiment(experiment, options = { reset: true })
48
48
  return false if active_experiments[experiment.name].nil?
49
49
  return true if experiment.has_winner?
50
50
  should_reset = experiment.resettable? && options[:reset]
51
51
  if ab_user[experiment.finished_key] && !should_reset
52
- return true
52
+ true
53
53
  else
54
54
  alternative_name = ab_user[experiment.key]
55
55
  trial = Trial.new(
56
- :user => ab_user,
57
- :experiment => experiment,
58
- :alternative => alternative_name,
59
- :goals => options[:goals],
60
- )
61
-
56
+ user: ab_user,
57
+ experiment: experiment,
58
+ alternative: alternative_name,
59
+ goals: options[:goals],
60
+ )
61
+
62
62
  trial.complete!(self)
63
63
 
64
64
  if should_reset
@@ -69,7 +69,7 @@ module Split
69
69
  end
70
70
  end
71
71
 
72
- def ab_finished(metric_descriptor, options = {:reset => true})
72
+ def ab_finished(metric_descriptor, options = { reset: true })
73
73
  return if exclude_visitor? || Split.configuration.disabled?
74
74
  metric_descriptor, goals = normalize_metric(metric_descriptor)
75
75
  experiments = Metric.possible_experiments(metric_descriptor)
@@ -77,7 +77,7 @@ module Split
77
77
  if experiments.any?
78
78
  experiments.each do |experiment|
79
79
  next if override_present?(experiment.key)
80
- finish_experiment(experiment, options.merge(:goals => goals))
80
+ finish_experiment(experiment, options.merge(goals: goals))
81
81
  end
82
82
  end
83
83
  rescue => e
@@ -95,7 +95,7 @@ module Split
95
95
  alternative_name = ab_user[experiment.key]
96
96
 
97
97
  if alternative_name
98
- alternative = experiment.alternatives.find{|alt| alt.name == alternative_name}
98
+ alternative = experiment.alternatives.find { |alt| alt.name == alternative_name }
99
99
  alternative.record_extra_info(key, value) if alternative
100
100
  end
101
101
  end
@@ -105,7 +105,7 @@ module Split
105
105
  Split.configuration.db_failover_on_db_error.call(e)
106
106
  end
107
107
 
108
- def ab_active_experiments()
108
+ def ab_active_experiments
109
109
  ab_user.active_experiments
110
110
  rescue => e
111
111
  raise unless Split.configuration.db_failover
@@ -127,14 +127,14 @@ module Split
127
127
  def override_alternative_by_cookies(experiment_name)
128
128
  return unless defined?(request)
129
129
 
130
- if request.cookies && request.cookies.key?('split_override')
131
- experiments = JSON.parse(request.cookies['split_override']) rescue {}
130
+ if request.cookies && request.cookies.key?("split_override")
131
+ experiments = JSON.parse(request.cookies["split_override"]) rescue {}
132
132
  experiments[experiment_name]
133
133
  end
134
134
  end
135
135
 
136
136
  def split_generically_disabled?
137
- defined?(params) && params['SPLIT_DISABLE']
137
+ defined?(params) && params["SPLIT_DISABLE"]
138
138
  end
139
139
 
140
140
  def ab_user
@@ -150,7 +150,7 @@ module Split
150
150
  end
151
151
 
152
152
  def is_preview?
153
- defined?(request) && defined?(request.headers) && request.headers['x-purpose'] == 'preview'
153
+ defined?(request) && defined?(request.headers) && request.headers["x-purpose"] == "preview"
154
154
  end
155
155
 
156
156
  def is_ignored_ip_address?
data/lib/split/metric.rb CHANGED
@@ -16,13 +16,13 @@ module Split
16
16
  def self.load_from_redis(name)
17
17
  metric = Split.redis.hget(:metrics, name)
18
18
  if metric
19
- experiment_names = metric.split(',')
19
+ experiment_names = metric.split(",")
20
20
 
21
21
  experiments = experiment_names.collect do |experiment_name|
22
22
  Split::ExperimentCatalog.find(experiment_name)
23
23
  end
24
24
 
25
- Split::Metric.new(:name => name, :experiments => experiments)
25
+ Split::Metric.new(name: name, experiments: experiments)
26
26
  else
27
27
  nil
28
28
  end
@@ -31,7 +31,7 @@ module Split
31
31
  def self.load_from_configuration(name)
32
32
  metrics = Split.configuration.metrics
33
33
  if metrics && metrics[name]
34
- Split::Metric.new(:experiments => metrics[name], :name => name)
34
+ Split::Metric.new(experiments: metrics[name], name: name)
35
35
  else
36
36
  nil
37
37
  end
@@ -77,7 +77,7 @@ module Split
77
77
  end
78
78
 
79
79
  def save
80
- Split.redis.hset(:metrics, name, experiments.map(&:name).join(','))
80
+ Split.redis.hset(:metrics, name, experiments.map(&:name).join(","))
81
81
  end
82
82
 
83
83
  def complete!
@@ -97,6 +97,5 @@ module Split
97
97
  return metric_name, goals
98
98
  end
99
99
  private_class_method :normalize_metric
100
-
101
100
  end
102
101
  end
@@ -5,7 +5,6 @@ require "json"
5
5
  module Split
6
6
  module Persistence
7
7
  class CookieAdapter
8
-
9
8
  def initialize(context)
10
9
  @context = context
11
10
  @request, @response = context.request, context.response
@@ -30,50 +29,49 @@ module Split
30
29
  end
31
30
 
32
31
  private
32
+ def set_cookie(value = {})
33
+ cookie_key = :split.to_s
34
+ cookie_value = default_options.merge(value: JSON.generate(value))
35
+ if action_dispatch?
36
+ # The "send" is necessary when we call ab_test from the controller
37
+ # and thus @context is a rails controller, because then "cookies" is
38
+ # a private method.
39
+ @context.send(:cookies)[cookie_key] = cookie_value
40
+ else
41
+ set_cookie_via_rack(cookie_key, cookie_value)
42
+ end
43
+ end
33
44
 
34
- def set_cookie(value = {})
35
- cookie_key = :split.to_s
36
- cookie_value = default_options.merge(value: JSON.generate(value))
37
- if action_dispatch?
38
- # The "send" is necessary when we call ab_test from the controller
39
- # and thus @context is a rails controller, because then "cookies" is
40
- # a private method.
41
- @context.send(:cookies)[cookie_key] = cookie_value
42
- else
43
- set_cookie_via_rack(cookie_key, cookie_value)
45
+ def default_options
46
+ { expires: @expires, path: "/", domain: cookie_domain_config }.compact
44
47
  end
45
- end
46
48
 
47
- def default_options
48
- { expires: @expires, path: '/', domain: cookie_domain_config }.compact
49
- end
49
+ def set_cookie_via_rack(key, value)
50
+ delete_cookie_header!(@response.header, key, value)
51
+ Rack::Utils.set_cookie_header!(@response.header, key, value)
52
+ end
50
53
 
51
- def set_cookie_via_rack(key, value)
52
- delete_cookie_header!(@response.header, key, value)
53
- Rack::Utils.set_cookie_header!(@response.header, key, value)
54
- end
54
+ # Use Rack::Utils#make_delete_cookie_header after Rack 2.0.0
55
+ def delete_cookie_header!(header, key, value)
56
+ cookie_header = header["Set-Cookie"]
57
+ case cookie_header
58
+ when nil, ""
59
+ cookies = []
60
+ when String
61
+ cookies = cookie_header.split("\n")
62
+ when Array
63
+ cookies = cookie_header
64
+ end
55
65
 
56
- # Use Rack::Utils#make_delete_cookie_header after Rack 2.0.0
57
- def delete_cookie_header!(header, key, value)
58
- cookie_header = header['Set-Cookie']
59
- case cookie_header
60
- when nil, ''
61
- cookies = []
62
- when String
63
- cookies = cookie_header.split("\n")
64
- when Array
65
- cookies = cookie_header
66
+ cookies.reject! { |cookie| cookie =~ /\A#{Rack::Utils.escape(key)}=/ }
67
+ header["Set-Cookie"] = cookies.join("\n")
66
68
  end
67
69
 
68
- cookies.reject! { |cookie| cookie =~ /\A#{Rack::Utils.escape(key)}=/ }
69
- header['Set-Cookie'] = cookies.join("\n")
70
- end
71
-
72
- def hash
73
- @hash ||= begin
74
- if cookies = @cookies[:split.to_s]
70
+ def hash
71
+ @hash ||= if cookies = @cookies[:split.to_s]
75
72
  begin
76
- JSON.parse(cookies)
73
+ parsed = JSON.parse(cookies)
74
+ parsed.is_a?(Hash) ? parsed : {}
77
75
  rescue JSON::ParserError
78
76
  {}
79
77
  end
@@ -81,19 +79,18 @@ module Split
81
79
  {}
82
80
  end
83
81
  end
84
- end
85
82
 
86
- def cookie_length_config
87
- Split.configuration.persistence_cookie_length
88
- end
83
+ def cookie_length_config
84
+ Split.configuration.persistence_cookie_length
85
+ end
89
86
 
90
- def cookie_domain_config
91
- Split.configuration.persistence_cookie_domain
92
- end
87
+ def cookie_domain_config
88
+ Split.configuration.persistence_cookie_domain
89
+ end
93
90
 
94
- def action_dispatch?
95
- defined?(Rails) && @response.is_a?(ActionDispatch::Response)
96
- end
91
+ def action_dispatch?
92
+ defined?(Rails) && @response.is_a?(ActionDispatch::Response)
93
+ end
97
94
  end
98
95
  end
99
96
  end
@@ -3,7 +3,7 @@
3
3
  module Split
4
4
  module Persistence
5
5
  class DualAdapter
6
- def self.with_config(options={})
6
+ def self.with_config(options = {})
7
7
  self.config.merge!(options)
8
8
  self
9
9
  end
@@ -72,14 +72,13 @@ module Split
72
72
  end
73
73
 
74
74
  private
75
+ def decrement_participation?(old_value, value)
76
+ !old_value.nil? && !value.nil? && old_value != value
77
+ end
75
78
 
76
- def decrement_participation?(old_value, value)
77
- !old_value.nil? && !value.nil? && old_value != value
78
- end
79
-
80
- def decrement_participation(key, value)
81
- Split.redis.hincrby("#{key}:#{value}", 'participant_count', -1)
82
- end
79
+ def decrement_participation(key, value)
80
+ Split.redis.hincrby("#{key}:#{value}", "participant_count", -1)
81
+ end
83
82
  end
84
83
  end
85
84
  end
@@ -3,7 +3,7 @@
3
3
  module Split
4
4
  module Persistence
5
5
  class RedisAdapter
6
- DEFAULT_CONFIG = {:namespace => 'persistence'}.freeze
6
+ DEFAULT_CONFIG = { namespace: "persistence" }.freeze
7
7
 
8
8
  attr_reader :redis_key
9
9
 
@@ -44,7 +44,7 @@ module Split
44
44
  new(nil, user_id)
45
45
  end
46
46
 
47
- def self.with_config(options={})
47
+ def self.with_config(options = {})
48
48
  self.config.merge!(options)
49
49
  self
50
50
  end
@@ -56,7 +56,6 @@ module Split
56
56
  def self.reset_config!
57
57
  @config = DEFAULT_CONFIG.dup
58
58
  end
59
-
60
59
  end
61
60
  end
62
61
  end
@@ -3,7 +3,6 @@
3
3
  module Split
4
4
  module Persistence
5
5
  class SessionAdapter
6
-
7
6
  def initialize(context)
8
7
  @session = context.session
9
8
  @session[:split] ||= {}
@@ -24,7 +23,6 @@ module Split
24
23
  def keys
25
24
  @session[:split].keys
26
25
  end
27
-
28
26
  end
29
27
  end
30
28
  end
@@ -2,10 +2,10 @@
2
2
 
3
3
  module Split
4
4
  module Persistence
5
- require 'split/persistence/cookie_adapter'
6
- require 'split/persistence/dual_adapter'
7
- require 'split/persistence/redis_adapter'
8
- require 'split/persistence/session_adapter'
5
+ require "split/persistence/cookie_adapter"
6
+ require "split/persistence/dual_adapter"
7
+ require "split/persistence/redis_adapter"
8
+ require "split/persistence/session_adapter"
9
9
 
10
10
  ADAPTERS = {
11
11
  cookie: Split::Persistence::CookieAdapter,
@@ -24,7 +24,6 @@ module Split
24
24
  end
25
25
 
26
26
  private
27
-
28
- attr_accessor :redis
27
+ attr_accessor :redis
29
28
  end
30
29
  end
data/lib/split/trial.rb CHANGED
@@ -24,15 +24,15 @@ module Split
24
24
 
25
25
  def alternative
26
26
  @alternative ||= if @experiment.has_winner?
27
- @experiment.winner
28
- end
27
+ @experiment.winner
28
+ end
29
29
  end
30
30
 
31
31
  def alternative=(alternative)
32
32
  @alternative = if alternative.kind_of?(Split::Alternative)
33
33
  alternative
34
34
  else
35
- @experiment.alternatives.find{|a| a.name == alternative }
35
+ @experiment.alternatives.find { |a| a.name == alternative }
36
36
  end
37
37
  end
38
38
 
@@ -41,7 +41,7 @@ module Split
41
41
  if Array(goals).empty?
42
42
  alternative.increment_completion
43
43
  else
44
- Array(goals).each {|g| alternative.increment_completion(g) }
44
+ Array(goals).each { |g| alternative.increment_completion(g) }
45
45
  end
46
46
 
47
47
  run_callback context, Split.configuration.on_trial_complete
@@ -97,31 +97,30 @@ module Split
97
97
  end
98
98
 
99
99
  private
100
+ def run_callback(context, callback_name)
101
+ context.send(callback_name, self) if callback_name && context.respond_to?(callback_name, true)
102
+ end
100
103
 
101
- def run_callback(context, callback_name)
102
- context.send(callback_name, self) if callback_name && context.respond_to?(callback_name, true)
103
- end
104
-
105
- def override_is_alternative?
106
- @experiment.alternatives.map(&:name).include?(@options[:override])
107
- end
104
+ def override_is_alternative?
105
+ @experiment.alternatives.map(&:name).include?(@options[:override])
106
+ end
108
107
 
109
- def should_store_alternative?
110
- if @options[:override] || @options[:disabled]
111
- Split.configuration.store_override
112
- else
113
- !exclude_user?
108
+ def should_store_alternative?
109
+ if @options[:override] || @options[:disabled]
110
+ Split.configuration.store_override
111
+ else
112
+ !exclude_user?
113
+ end
114
114
  end
115
- end
116
115
 
117
- def cleanup_old_versions
118
- if @experiment.version > 0
119
- @user.cleanup_old_versions!(@experiment)
116
+ def cleanup_old_versions
117
+ if @experiment.version > 0
118
+ @user.cleanup_old_versions!(@experiment)
119
+ end
120
120
  end
121
- end
122
121
 
123
- def exclude_user?
124
- @options[:exclude] || @experiment.start_time.nil? || @user.max_experiments_reached?(@experiment.key)
125
- end
122
+ def exclude_user?
123
+ @options[:exclude] || @experiment.start_time.nil? || @user.max_experiments_reached?(@experiment.key)
124
+ end
126
125
  end
127
126
  end
data/lib/split/user.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'forwardable'
3
+ require "forwardable"
4
4
 
5
5
  module Split
6
6
  class User
@@ -26,10 +26,10 @@ module Split
26
26
  end
27
27
 
28
28
  def max_experiments_reached?(experiment_key)
29
- if Split.configuration.allow_multiple_experiments == 'control'
29
+ if Split.configuration.allow_multiple_experiments == "control"
30
30
  experiments = active_experiments
31
31
  experiment_key_without_version = key_without_version(experiment_key)
32
- count_control = experiments.count {|k, v| k == experiment_key_without_version || v == 'control'}
32
+ count_control = experiments.count { |k, v| k == experiment_key_without_version || v == "control" }
33
33
  experiments.size > count_control
34
34
  else
35
35
  !Split.configuration.allow_multiple_experiments &&
@@ -65,17 +65,16 @@ module Split
65
65
  end
66
66
 
67
67
  private
68
+ def keys_without_experiment(keys, experiment_key)
69
+ keys.reject { |k| k.match(Regexp.new("^#{experiment_key}(:finished)?$")) }
70
+ end
68
71
 
69
- def keys_without_experiment(keys, experiment_key)
70
- keys.reject { |k| k.match(Regexp.new("^#{experiment_key}(:finished)?$")) }
71
- end
72
-
73
- def keys_without_finished(keys)
74
- keys.reject { |k| k.include?(":finished") }
75
- end
72
+ def keys_without_finished(keys)
73
+ keys.reject { |k| k.include?(":finished") }
74
+ end
76
75
 
77
- def key_without_version(key)
78
- key.split(/\:\d(?!\:)/)[0]
79
- end
76
+ def key_without_version(key)
77
+ key.split(/\:\d(?!\:)/)[0]
78
+ end
80
79
  end
81
80
  end
data/lib/split/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Split
4
- VERSION = "4.0.1"
4
+ VERSION = "4.0.2"
5
5
  end
data/lib/split/zscore.rb CHANGED
@@ -2,7 +2,6 @@
2
2
 
3
3
  module Split
4
4
  class Zscore
5
-
6
5
  include Math
7
6
 
8
7
  def self.calculate(p1, n1, p2, n2)
@@ -51,8 +50,7 @@ module Split
51
50
  # Calculate z-score
52
51
  z_score = (p_1 - p_2)/(se)
53
52
 
54
- return z_score
55
-
53
+ z_score
56
54
  end
57
55
  end
58
56
  end