class EenyMeeny::Middleware

Constants

Headers

QUERY_STRING
REQUEST_METHOD

Public Class Methods

new(app) click to toggle source
# File lib/eeny-meeny/middleware.rb, line 16
def initialize(app)
  @app = app
  @experiments = EenyMeeny::Experiment.find_all
  @cookie_config = EenyMeeny.config.cookies
end

Public Instance Methods

call(env) click to toggle source
# File lib/eeny-meeny/middleware.rb, line 22
def call(env)
  cookies          = Rack::Utils.parse_query(env[HTTP_COOKIE],';,')  { |s| Rack::Utils.unescape(s) rescue s }
  query_parameters = query_hash(env)
  now              = Time.zone.now
  new_cookies      = {}
  delete_cookies   = find_deprecated_cookies(cookies, now)
  # Prepare for experiments.
  @experiments.each do |experiment|
    # Skip inactive experiments
    next unless experiment.active?(now)
    env, new_cookies = prepare_experiment(env, cookies, new_cookies, query_parameters, experiment)
  end
  # Prepare smoke tests
  env, new_cookies = prepare_smoke_test(env, new_cookies, query_parameters)
  # Delegate to app
  status, headers, body = @app.call(env)
  response = Rack::Response.new(body, status, headers)
  # Add new cookies to 'Set-Cookie' header
  new_cookies.each do |key, value|
    response.set_cookie(key,value.to_h)
  end
  delete_cookies.each do |key, value|
    response.delete_cookie(key, value: value, path: @cookie_config[:path], same_site: @cookie_config[:same_site])
  end
  response.finish
end

Private Instance Methods

find_deprecated_cookies(cookies, now) click to toggle source
# File lib/eeny-meeny/middleware.rb, line 51
def find_deprecated_cookies(cookies, now)
  deprecated_cookies = {}
  cookies.each do |cookie_name, value|
    # Skip any cookie that does not have the 'eeny_meeny_' prefix
    next unless cookie_name.to_s.start_with?(EenyMeeny::Cookie::EXPERIMENT_PREFIX)
    # Mark cookies that does not match any existing active experiment as 'deprecated'.
    experiment = EenyMeeny::Experiment.find_by_cookie_name(cookie_name)
    next if experiment && experiment.active?(now)
    deprecated_cookies[cookie_name] = value
  end
  deprecated_cookies
end
prepare_experiment(env, cookies, new_cookies, query_parameters, experiment) click to toggle source
# File lib/eeny-meeny/middleware.rb, line 64
def prepare_experiment(env, cookies, new_cookies, query_parameters, experiment)
  # Trigger experiment through query parameters
  cookie_name = EenyMeeny::Cookie.cookie_name(experiment)
  has_experiment_trigger = EenyMeeny.config.query_parameters[:experiment] && query_parameters.key?(cookie_name)
  # Skip experiments that already have a cookie
  return env, new_cookies unless has_experiment_trigger || !cookies.key?(cookie_name)
  cookie = if has_experiment_trigger
             # Trigger experiment variation through query parameter.
             EenyMeeny::Cookie.create_for_experiment_variation(experiment, query_parameters[cookie_name].to_sym, @cookie_config)
           else
             EenyMeeny::Cookie.create_for_experiment(experiment, @cookie_config)
           end
  # Set HTTP_COOKIE header to enable experiment on first pageview
  env = add_or_replace_http_cookie(env, cookie)
  new_cookies[cookie.name] = cookie
  return env, new_cookies
end
prepare_smoke_test(env, new_cookies, query_parameters) click to toggle source
# File lib/eeny-meeny/middleware.rb, line 82
def prepare_smoke_test(env, new_cookies, query_parameters)
  # Skip if the EenyMeeny 'smoke test' query parameters configuration is disabled.
  return env, new_cookies unless EenyMeeny.config.query_parameters[:smoke_test]
  # Skip if no valid 'smoke_test_id' query parameter present
  return env, new_cookies unless query_parameters.key?('smoke_test_id') && (query_parameters['smoke_test_id'] =~ /\A[A-Za-z_]+\z/)
  # Set HTTP_COOKIE header to enable smoke test on first pageview
  cookie = EenyMeeny::Cookie.create_for_smoke_test(query_parameters['smoke_test_id'], **@cookie_config)
  env = add_or_replace_http_cookie(env, cookie)
  new_cookies[cookie.name] = cookie
  return env, new_cookies
end
query_hash(env) click to toggle source
# File lib/eeny-meeny/middleware.rb, line 94
def query_hash(env)
  # Query Params are only relevant if EenyMeeny.config have them enabled.
  return {} unless EenyMeeny.config.query_parameters[:experiment] || EenyMeeny.config.query_parameters[:smoke_test]
  # Query Params are only relevant to HTTP GET requests.
  return {} unless env[REQUEST_METHOD] == 'GET'
  Rack::Utils.parse_query(env[QUERY_STRING], '&;')
end