Module: Split::Helper

Included in:
EncapsulatedHelper::ContextShim
Defined in:
lib/split/helper.rb

Constant Summary collapse

OVERRIDE_PARAM_NAME =
"ab_test"

Class Method Summary collapse

Class Method Details

.ab_active_experimentsObject



108
109
110
111
112
113
# File 'lib/split/helper.rb', line 108

def ab_active_experiments
  ab_user.active_experiments
rescue => e
  raise unless Split.configuration.db_failover
  Split.configuration.db_failover_on_db_error.call(e)
end

.ab_finished(metric_descriptor, options = { reset: true }) ⇒ Object



72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
# File 'lib/split/helper.rb', line 72

def ab_finished(metric_descriptor, options = { reset: true })
  return if exclude_visitor? || Split.configuration.disabled?
  metric_descriptor, goals = normalize_metric(metric_descriptor)
  experiments = Metric.possible_experiments(metric_descriptor)

  if experiments.any?
    experiments.each do |experiment|
      next if override_present?(experiment.key)
      finish_experiment(experiment, options.merge(goals: goals))
    end
  end
rescue => e
  raise unless Split.configuration.db_failover
  Split.configuration.db_failover_on_db_error.call(e)
end

.ab_record_extra_info(metric_descriptor, key, value = 1) ⇒ Object



88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
# File 'lib/split/helper.rb', line 88

def ab_record_extra_info(metric_descriptor, key, value = 1)
  return if exclude_visitor? || Split.configuration.disabled? || value.nil?
  metric_descriptor, _ = normalize_metric(metric_descriptor)
  experiments = Metric.possible_experiments(metric_descriptor)

  if experiments.any?
    experiments.each do |experiment|
      alternative_name = ab_user[experiment.key]

      if alternative_name
        alternative = experiment.alternatives.find { |alt| alt.name == alternative_name }
        alternative.record_extra_info(key, value) if alternative
      end
    end
  end
rescue => e
  raise unless Split.configuration.db_failover
  Split.configuration.db_failover_on_db_error.call(e)
end

.ab_test(metric_descriptor, control = nil, *alternatives) ⇒ Object



9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# File 'lib/split/helper.rb', line 9

def ab_test(metric_descriptor, control = nil, *alternatives)
  begin
    experiment = ExperimentCatalog.find_or_initialize(metric_descriptor, control, *alternatives)
    alternative = if Split.configuration.enabled && !exclude_visitor?
      experiment.save
      raise(Split::InvalidExperimentsFormatError) unless (Split.configuration.experiments || {}).fetch(experiment.name.to_sym, {})[:combined_experiments].nil?
      trial = Trial.new(user: ab_user, experiment: experiment,
          override: override_alternative(experiment.name), exclude: exclude_visitor?,
          disabled: split_generically_disabled?)
      alt = trial.choose!(self)
      alt ? alt.name : nil
    else
      control_variable(experiment.control)
    end
  rescue Errno::ECONNREFUSED, Redis::BaseError, SocketError => e
    raise(e) unless Split.configuration.db_failover
    Split.configuration.db_failover_on_db_error.call(e)

    if Split.configuration.db_failover_allow_parameter_override
      alternative = override_alternative(experiment.name) if override_present?(experiment.name)
      alternative = control_variable(experiment.control) if split_generically_disabled?
    end
  ensure
    alternative ||= control_variable(experiment.control)
  end

  if block_given?
     = experiment.[alternative] if experiment.
    yield(alternative,  || {})
  else
    alternative
  end
end

.ab_userObject



140
141
142
# File 'lib/split/helper.rb', line 140

def ab_user
  @ab_user ||= User.new(self)
end

.active_experimentsObject



165
166
167
# File 'lib/split/helper.rb', line 165

def active_experiments
  ab_user.active_experiments
end

.control_variable(control) ⇒ Object



180
181
182
# File 'lib/split/helper.rb', line 180

def control_variable(control)
  Hash === control ? control.keys.first.to_s : control.to_s
end

.exclude_visitor?Boolean

Returns:

  • (Boolean)


144
145
146
# File 'lib/split/helper.rb', line 144

def exclude_visitor?
  defined?(request) && (instance_exec(request, &Split.configuration.ignore_filter) || is_ignored_ip_address? || is_robot? || is_preview?)
end

.finish_experiment(experiment, options = { reset: true }) ⇒ Object



47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# File 'lib/split/helper.rb', line 47

def finish_experiment(experiment, options = { reset: true })
  return false if active_experiments[experiment.name].nil?
  return true if experiment.has_winner?
  should_reset = experiment.resettable? && options[:reset]
  if ab_user[experiment.finished_key] && !should_reset
    true
  else
    alternative_name = ab_user[experiment.key]
    trial = Trial.new(
      user: ab_user,
      experiment: experiment,
      alternative: alternative_name,
      goals: options[:goals],
    )

    trial.complete!(self)

    if should_reset
      reset!(experiment)
    else
      ab_user[experiment.finished_key] = true
    end
  end
end

.is_ignored_ip_address?Boolean

Returns:

  • (Boolean)


156
157
158
159
160
161
162
163
# File 'lib/split/helper.rb', line 156

def is_ignored_ip_address?
  return false if Split.configuration.ignore_ip_addresses.empty?

  Split.configuration.ignore_ip_addresses.each do |ip|
    return true if defined?(request) && (request.ip == ip || (ip.class == Regexp && request.ip =~ ip))
  end
  false
end

.is_preview?Boolean

Returns:

  • (Boolean)


152
153
154
# File 'lib/split/helper.rb', line 152

def is_preview?
  defined?(request) && defined?(request.headers) && request.headers["x-purpose"] == "preview"
end

.is_robot?Boolean

Returns:

  • (Boolean)


148
149
150
# File 'lib/split/helper.rb', line 148

def is_robot?
  defined?(request) && request.user_agent =~ Split.configuration.robot_regex
end

.normalize_metric(metric_descriptor) ⇒ Object



169
170
171
172
173
174
175
176
177
178
# File 'lib/split/helper.rb', line 169

def normalize_metric(metric_descriptor)
  if Hash === metric_descriptor
    experiment_name = metric_descriptor.keys.first
    goals = Array(metric_descriptor.values.first)
  else
    experiment_name = metric_descriptor
    goals = []
  end
  return experiment_name, goals
end

.override_alternative(experiment_name) ⇒ Object



119
120
121
# File 'lib/split/helper.rb', line 119

def override_alternative(experiment_name)
  override_alternative_by_params(experiment_name) || override_alternative_by_cookies(experiment_name)
end

.override_alternative_by_cookies(experiment_name) ⇒ Object



127
128
129
130
131
132
133
134
# File 'lib/split/helper.rb', line 127

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 {}
    experiments[experiment_name]
  end
end

.override_alternative_by_params(experiment_name) ⇒ Object



123
124
125
# File 'lib/split/helper.rb', line 123

def override_alternative_by_params(experiment_name)
  defined?(params) && params[OVERRIDE_PARAM_NAME] && params[OVERRIDE_PARAM_NAME][experiment_name]
end

.override_present?(experiment_name) ⇒ Boolean

Returns:

  • (Boolean)


115
116
117
# File 'lib/split/helper.rb', line 115

def override_present?(experiment_name)
  override_alternative_by_params(experiment_name) || override_alternative_by_cookies(experiment_name)
end

.reset!(experiment) ⇒ Object



43
44
45
# File 'lib/split/helper.rb', line 43

def reset!(experiment)
  ab_user.delete(experiment.key)
end

.split_generically_disabled?Boolean

Returns:

  • (Boolean)


136
137
138
# File 'lib/split/helper.rb', line 136

def split_generically_disabled?
  defined?(params) && params["SPLIT_DISABLE"]
end