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



105
106
107
108
109
110
# File 'lib/split/helper.rb', line 105

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



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

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|
      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



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

def ab_record_extra_info(metric_descriptor, key, value = 1)
  return if exclude_visitor? || Split.configuration.disabled?
  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



8
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
# File 'lib/split/helper.rb', line 8

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?
      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?
     = trial ? trial. : {}
    yield(alternative, )
  else
    alternative
  end
end

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



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

def self.ab_test_config(metric_descriptor, control = nil, *alternatives)
  experiment = ExperimentCatalog.find_or_initialize(metric_descriptor, control, *alternatives)
  experiment.save
end

.ab_userObject



125
126
127
# File 'lib/split/helper.rb', line 125

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

.active_experimentsObject



150
151
152
# File 'lib/split/helper.rb', line 150

def active_experiments
  ab_user.active_experiments
end

.control_variable(control) ⇒ Object



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

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

.exclude_visitor?Boolean

Returns:

  • (Boolean)


129
130
131
# File 'lib/split/helper.rb', line 129

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



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

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
    return true
  else
    alternative_name = ab_user[experiment.key]
    trial = Trial.new(:user => ab_user, :experiment => experiment,
          :alternative => alternative_name)
    trial.complete!(options[:goals], self)

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

.is_ignored_ip_address?Boolean

Returns:

  • (Boolean)


141
142
143
144
145
146
147
148
# File 'lib/split/helper.rb', line 141

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)


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

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

.is_robot?Boolean

Returns:

  • (Boolean)


133
134
135
# File 'lib/split/helper.rb', line 133

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

.normalize_metric(metric_descriptor) ⇒ Object



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

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



117
118
119
# File 'lib/split/helper.rb', line 117

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

.override_present?(experiment_name) ⇒ Boolean

Returns:

  • (Boolean)


113
114
115
# File 'lib/split/helper.rb', line 113

def override_present?(experiment_name)
  override_alternative(experiment_name)
end

.reset!(experiment) ⇒ Object



46
47
48
# File 'lib/split/helper.rb', line 46

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

.split_generically_disabled?Boolean

Returns:

  • (Boolean)


121
122
123
# File 'lib/split/helper.rb', line 121

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