class TrailGuide::Helper::ExperimentProxy

Attributes

key[R]

Public Class Methods

new(context, key, **opts) click to toggle source
Calls superclass method
# File lib/trail_guide/helper/experiment_proxy.rb, line 6
def initialize(context, key, **opts)
  super(context, **opts)
  @key = key.to_s.underscore.to_sym
end

Public Instance Methods

choose(**opts, &block) click to toggle source
# File lib/trail_guide/helper/experiment_proxy.rb, line 25
def choose(**opts, &block)
  choose!(**opts, &block)
rescue NoExperimentsError => e
  raise e
rescue => e
  TrailGuide.logger.error e
  experiment.control
end
Also aliased as: enroll
choose!(**opts) { |variant, opts| ... } click to toggle source
# File lib/trail_guide/helper/experiment_proxy.rb, line 11
def choose!(**opts, &block)
  raise NoExperimentsError, key if experiments.empty?
  raise TooManyExperimentsError, "Selecting a variant requires a single experiment, but `#{key}` matches more than one experiment." if experiments.length > 1
  raise TooManyExperimentsError, "Selecting a variant requires a single experiment, but `#{key}` refers to a combined experiment." if experiment.combined?
  opts = {override: override_variant, excluded: exclude_visitor?}.merge(opts)
  variant = experiment.choose!(**opts)
  if block_given?
    yield variant, opts[:metadata]
  else
    variant
  end
end
Also aliased as: enroll!
convert(checkpoint=nil, **opts, &block) click to toggle source
# File lib/trail_guide/helper/experiment_proxy.rb, line 126
def convert(checkpoint=nil, **opts, &block)
  convert!(checkpoint, **opts, &block)
rescue => e
  TrailGuide.logger.error e
  false
end
convert!(checkpoint=nil, **opts) { |checkpoints, opts| ... } click to toggle source
# File lib/trail_guide/helper/experiment_proxy.rb, line 96
def convert!(checkpoint=nil, **opts, &block)
  raise NoExperimentsError, key if experiments.empty?
  checkpoints = experiments.map do |experiment|
    ckpt = checkpoint || experiment.goals.find { |g| g == key }
    if experiment.combined?
      experiment.combined_experiments.map do |combo|
        combo.convert!(ckpt, **opts)
      end
    else
      experiment.convert!(ckpt, **opts)
    end
  end.flatten

  return false unless checkpoints.any?

  if block_given?
    yield checkpoints, opts[:metadata]
  else
    checkpoints
  end
rescue NoExperimentsError => e
  unless TrailGuide.configuration.ignore_orphaned_groups?
    trace = e.backtrace.find { |t| !t.match?(Regexp.new(File.dirname(__FILE__))) }
      .to_s.split(Rails.root.to_s).last
      .split(':').first(2).join(':')
    TrailGuide.catalog.orphaned(key, trace)
  end
  false
end
enroll(**opts, &block)
Alias for: choose
enroll!(**opts, &block)
Alias for: choose!
exclude_visitor?() click to toggle source
# File lib/trail_guide/helper/experiment_proxy.rb, line 154
def exclude_visitor?
  return false if experiment.configuration.skip_request_filter?
  context.send(:trailguide_excluded_request?)
end
experiment() click to toggle source
# File lib/trail_guide/helper/experiment_proxy.rb, line 139
def experiment
  @experiment ||= experiments.first
end
experiments() click to toggle source
# File lib/trail_guide/helper/experiment_proxy.rb, line 133
def experiments
  @experiments ||= TrailGuide.catalog.select(key).map do |experiment|
    experiment.new(participant)
  end
end
override_variant() click to toggle source
# File lib/trail_guide/helper/experiment_proxy.rb, line 143
def override_variant
  return unless context.respond_to?(:trailguide_params, true) || context.respond_to?(:params, true)
  params = context.try(:trailguide_params) || context.try(:params)
  return unless params.key?(TrailGuide.configuration.override_parameter)
  experiment_params = params[TrailGuide.configuration.override_parameter]
  return unless experiment_params.key?(experiment.experiment_name.to_s)
  varname = experiment_params[experiment.experiment_name.to_s]
  variant = experiment.variants.find { |var| var == varname }
  variant.try(:name)
end
render(prefix: nil, templates: nil, locals: {}, **opts) click to toggle source
# File lib/trail_guide/helper/experiment_proxy.rb, line 87
def render(prefix: nil, templates: nil, locals: {}, **opts)
  render!(prefix: prefix, templates: templates, locals: locals, **opts)
rescue NoExperimentsError => e
  raise e
rescue => e
  TrailGuide.logger.error e
  false
end
render!(prefix: nil, templates: nil, locals: {}, **opts) click to toggle source
# File lib/trail_guide/helper/experiment_proxy.rb, line 73
def render!(prefix: nil, templates: nil, locals: {}, **opts)
  raise UnsupportedContextError, "The current context (#{context}) does not support rendering. Rendering is only available in controllers and views." unless context.respond_to?(:render, true)
  choose!(**opts) do |variant, metadata|
    locals = { variant: variant, metadata: variant.metadata }.merge(locals)
    locals = { locals: locals } if context_type == :controller

    template = templates[variant.name] if templates
    prefix ||= (context.try(:view_context) || context).lookup_context.prefixes.first + '/' rescue ''
    template ||= "#{prefix.to_s}#{variant.experiment.experiment_name.to_s.underscore}/#{variant.name.to_s.underscore}"

    context.send(:render, template.to_s, **locals)
  end
end
run(methods: nil, **opts) click to toggle source
# File lib/trail_guide/helper/experiment_proxy.rb, line 64
def run(methods: nil, **opts)
  run!(methods: methods, **opts)
rescue NoExperimentsError => e
  raise e
rescue => e
  TrailGuide.logger.error e
  false
end
run!(methods: nil, **opts) click to toggle source
# File lib/trail_guide/helper/experiment_proxy.rb, line 35
def run!(methods: nil, **opts)
  choose!(**opts) do |variant, metadata|
    varmeth = methods[variant.name] if methods
    varmeth ||= variant.name

    unless context.respond_to?(varmeth, true)
      if context_type == :controller
        raise NoVariantMethodError,
          "Undefined local method `#{varmeth}`. You must define a controller method matching the variant `#{variant.name}` in your experiment `#{key}`. In this case it looks like you need to define #{context.class.name}##{varmeth}(metadata={})"
      elsif context_type == :template
        raise NoVariantMethodError,
          "Undefined local method `#{varmeth}`. You must define a helper method matching the variant `#{variant.name}` in your experiment `#{key}`. In this case it looks like you need to define ApplicationHelper##{varmeth}(metadata={})"
      else
        raise NoVariantMethodError,
          "Undefined local method `#{varmeth}`. You must define a method matching the variant `#{variant.name}` in your experiment `#{key}`. In this case it looks like you need to define #{context.class.name}##{varmeth}(metadata={})"
      end
    end

    arguments = context.method(varmeth).parameters
    if arguments.empty?
      context.send(varmeth)
    elsif arguments.length > 1 || arguments[0][0] == :rest
      context.send(varmeth, variant, **variant.metadata)
    elsif arguments.length == 1
      context.send(varmeth, **variant.metadata)
    end
  end
end