Class: TrailGuide::Helper::MetricProxy

Inherits:
HelperProxy show all
Defined in:
lib/trail_guide/helper.rb

Instance Attribute Summary collapse

Attributes inherited from HelperProxy

#context

Instance Method Summary collapse

Methods inherited from HelperProxy

#context_type, #new, #participant

Constructor Details

#initialize(context, metric, **opts) ⇒ MetricProxy

Returns a new instance of MetricProxy.

Raises:



53
54
55
56
57
# File 'lib/trail_guide/helper.rb', line 53

def initialize(context, metric, **opts)
  super(context, **opts)
  @metric = metric
  raise NoExperimentsError, "Could not find any experiments matching `#{metric}`." if experiments.empty?
end

Instance Attribute Details

#metricObject (readonly)

Returns the value of attribute metric.



51
52
53
# File 'lib/trail_guide/helper.rb', line 51

def metric
  @metric
end

Instance Method Details

#choose!(**opts, &block) ⇒ Object



59
60
61
62
63
64
65
66
67
68
# File 'lib/trail_guide/helper.rb', line 59

def choose!(**opts, &block)
  raise TooManyExperimentsError, "Selecting a variant requires a single experiment, but the metric `#{metric}` matches more than one experiment." if experiments.length > 1
  opts = {override: override_variant, excluded: exclude_visitor?}.merge(opts)
  variant = experiment.choose!(**opts)
  if block_given?
    yield variant, opts[:metadata]
  else
    variant
  end
end

#convert!(checkpoint = nil, **opts, &block) ⇒ Object



113
114
115
116
117
118
119
120
121
# File 'lib/trail_guide/helper.rb', line 113

def convert!(checkpoint=nil, **opts, &block)
  checkpoints = experiments.map { |experiment| experiment.convert!(checkpoint, **opts) }
  return false unless checkpoints.any?
  if block_given?
    yield checkpoints, opts[:metadata]
  else
    checkpoints
  end
end

#exclude_visitor?Boolean

Returns:

  • (Boolean)


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

def exclude_visitor?
  instance_exec(context, &TrailGuide.configuration.request_filter)
end

#experimentObject



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

def experiment
  @experiment ||= experiments.first
end

#experimentsObject



123
124
125
126
127
# File 'lib/trail_guide/helper.rb', line 123

def experiments
  @experiments ||= TrailGuide::Catalog.select(metric).map do |experiment|
    experiment.new(participant)
  end
end

#is_filtered_ip_address?Boolean

Returns:

  • (Boolean)


168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
# File 'lib/trail_guide/helper.rb', line 168

def is_filtered_ip_address?
  return false if TrailGuide.configuration.filtered_ip_addresses.nil? || TrailGuide.configuration.filtered_ip_addresses.empty?

  return false unless context.respond_to?(:request, true)
  request = context.send(:request)
  return false unless request && request.ip

  TrailGuide.configuration.filtered_ip_addresses.each do |ip|
    return true if ip.class == String && request.ip == ip
    return true if ip.class == Regexp && request.ip =~ ip
    return true if ip.class == Range && ip.first.class == IPAddr && ip.include?(IPAddr.new(request.ip))
  end

  return false
end

#is_filtered_user_agent?Boolean

Returns:

  • (Boolean)


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

def is_filtered_user_agent?
  return false if TrailGuide.configuration.filtered_user_agents.nil? || TrailGuide.configuration.filtered_user_agents.empty?
  return false unless context.respond_to?(:request, true)
  request = context.send(:request)
  return false unless request && request.user_agent

  TrailGuide.configuration.filtered_user_agents do |ua|
    return true if ua.class == String && request.user_agent == ua
    return true if ua.class == Regexp && request.user_agent =~ ua
  end

  return false
end

#is_preview?Boolean

Returns:

  • (Boolean)


148
149
150
151
152
# File 'lib/trail_guide/helper.rb', line 148

def is_preview?
  return false unless context.respond_to?(:request, true)
  headers = context.send(:request).try(:headers)
  headers && headers['x-purpose'] == 'preview'
end

#override_variantObject



133
134
135
136
137
138
139
140
141
142
# File 'lib/trail_guide/helper.rb', line 133

def override_variant
  return unless context.respond_to?(:params, true)
  params = context.send(: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, **opts) ⇒ Object



99
100
101
102
103
104
105
106
107
108
109
110
111
# File 'lib/trail_guide/helper.rb', line 99

def render!(prefix: nil, templates: nil, **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, |
    locals = { variant: variant, metadata: variant. }
    locals = { locals: locals } if context_type == :controller

    template = templates[variant.name] if templates
    prefix ||= (context.try(:view_context) || context).lookup_context.prefixes.first + '/'
    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) ⇒ Object



70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
# File 'lib/trail_guide/helper.rb', line 70

def run!(methods: nil, **opts)
  choose!(**opts) do |variant, |
    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 `#{metric}`. 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 `#{metric}`. 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 `#{metric}`. 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.)
    elsif arguments.length == 1
      context.send(varmeth, **variant.)
    end
  end
end