eeny-meeny 2.1.1 → 2.1.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4599a9361b4a7d72a01d42b9b992b3f2578ead2e
4
- data.tar.gz: 43fbfdad8d15903e61717a2575da3c18c8063321
3
+ metadata.gz: bc5619bbf033d3480a5da8522c1f092330143d4b
4
+ data.tar.gz: 0b164df787564d745c45556f761eaa53fa0a3877
5
5
  SHA512:
6
- metadata.gz: 1037418a1485bd6bac1754280ff70230d600e23014b11ca97bf063bada61d548ffda7d4dd98fce93ec6ba8f457a2e2c6cb04d021ee45bab314b68f533b2fb80f
7
- data.tar.gz: 281d612ec907b728eab11f6d9621b22962e27715c112d41a0de8262107d7bca6edf2f945d85047bfa740a716c93e25bb5fa6994adf800d276852bb418b6e0448
6
+ metadata.gz: d617820ad4bb8200af9b0879f15349724ccae3522975128f6797eaa909bb6560a0d9c1ee6cc3ada1b45d9452c7be61f75a7fdbe53bafdadd505574326f30b330
7
+ data.tar.gz: fe02125d91074ddca8a019e071162f883d7459a079df9050c12d5016abeb6126653704b1ce00dc69b641f2f4e05c77ee0ba5a593b471d18b6175edfdba81d75a
@@ -0,0 +1,10 @@
1
+ language: ruby
2
+ sudo: false
3
+ env:
4
+ - "RAILS_VERSION=4.2.8"
5
+ rvm:
6
+ - 2.0.0
7
+ - 2.1.10
8
+ - 2.2.7
9
+ - 2.3.4
10
+ - 2.4.1
@@ -1,3 +1,14 @@
1
+ ### 2.1.2 (2017-04-14)
2
+
3
+ Bugfixes:
4
+
5
+ - Fix validation regex for the 'smoke_test_id' query parameter.
6
+
7
+ Other Changes:
8
+
9
+ - Clean up the way cookies are written to the HTTP_COOKIE header.
10
+ - Added Travis CI and build status badge.
11
+
1
12
  ### 2.1.1 (2016-10-06)
2
13
 
3
14
  Bugfixes:
data/README.md CHANGED
@@ -3,6 +3,7 @@ eeny-meeny
3
3
  [![Gem Version](https://badge.fury.io/rb/eeny-meeny.svg)](https://badge.fury.io/rb/eeny-meeny)
4
4
  [![Code Climate](https://codeclimate.com/github/corthmann/eeny-meeny/badges/gpa.svg)](https://codeclimate.com/github/corthmann/eeny-meeny)
5
5
  [![Test Coverage](https://codeclimate.com/github/corthmann/eeny-meeny/badges/coverage.svg)](https://codeclimate.com/github/corthmann/eeny-meeny/coverage)
6
+ [![Build Status](https://travis-ci.org/corthmann/eeny-meeny.svg?branch=master)](https://travis-ci.org/corthmann/eeny-meeny)
6
7
 
7
8
  Installation
8
9
  -------------
@@ -3,7 +3,7 @@ require File.expand_path('../lib/eeny-meeny/version', __FILE__)
3
3
  Gem::Specification.new do |s|
4
4
  s.name = 'eeny-meeny'
5
5
  s.version = EenyMeeny::VERSION.dup
6
- s.date = '2016-10-06'
6
+ s.date = '2017-04-14'
7
7
  s.summary = "A simple split and smoke testing tool for Rails"
8
8
  s.authors = ["Christian Orthmann"]
9
9
  s.email = 'christian.orthmann@gmail.com'
@@ -8,6 +8,11 @@ require 'eeny-meeny/models/cookie'
8
8
  module EenyMeeny
9
9
  class Middleware
10
10
 
11
+ # Headers
12
+ HTTP_COOKIE = 'HTTP_COOKIE'.freeze
13
+ REQUEST_METHOD = 'REQUEST_METHOD'.freeze
14
+ QUERY_STRING = 'QUERY_STRING'.freeze
15
+
11
16
  def initialize(app)
12
17
  @app = app
13
18
  @experiments = EenyMeeny::Experiment.find_all
@@ -15,46 +20,38 @@ module EenyMeeny
15
20
  end
16
21
 
17
22
  def call(env)
18
- request = Rack::Request.new(env)
19
- cookies = request.cookies
20
- now = Time.zone.now
21
- new_cookies = {}
22
- existing_set_cookie_header = env['Set-Cookie']
23
+ cookies = Rack::Utils.parse_query(env[HTTP_COOKIE],';,') { |s| Rack::Utils.unescape(s) rescue s }
24
+ query_parameters = query_hash(env)
25
+ now = Time.zone.now
26
+ new_cookies = {}
23
27
  # Prepare for experiments.
24
28
  @experiments.each do |experiment|
25
29
  # Skip inactive experiments
26
30
  next unless experiment.active?(now)
27
- # Trigger experiment through query parmeters
31
+ # Trigger experiment through query parameters
28
32
  cookie_name = EenyMeeny::Cookie.cookie_name(experiment)
29
- has_experiment_trigger = EenyMeeny.config.query_parameters[:experiment] && request.params.has_key?(cookie_name)
33
+ has_experiment_trigger = EenyMeeny.config.query_parameters[:experiment] && query_parameters.key?(cookie_name)
30
34
  # skip experiments that already have a cookie
31
- if has_experiment_trigger || !cookies.has_key?(cookie_name)
32
- cookie = if has_experiment_trigger
33
- # Trigger experiment variation through query parameter.
34
- EenyMeeny::Cookie.create_for_experiment_variation(experiment, request.params[cookie_name].to_sym, @cookie_config)
35
- else
36
- EenyMeeny::Cookie.create_for_experiment(experiment, @cookie_config)
37
- end
38
- # Set HTTP_COOKIE header to enable experiment on first pageview
39
- env = add_http_cookie(env, cookie, precede: has_experiment_trigger)
40
- new_cookies[cookie.name] = cookie
41
- end
35
+ next unless has_experiment_trigger || !cookies.key?(cookie_name)
36
+ cookie = if has_experiment_trigger
37
+ # Trigger experiment variation through query parameter.
38
+ EenyMeeny::Cookie.create_for_experiment_variation(experiment, query_parameters[cookie_name].to_sym, @cookie_config)
39
+ else
40
+ EenyMeeny::Cookie.create_for_experiment(experiment, @cookie_config)
41
+ end
42
+ # Set HTTP_COOKIE header to enable experiment on first pageview
43
+ env = add_or_replace_http_cookie(env, cookie)
44
+ new_cookies[cookie.name] = cookie
42
45
  end
43
46
  # Prepare smoke tests (if enabled through query parameters)
44
47
  if EenyMeeny.config.query_parameters[:smoke_test]
45
- if request.params.has_key?('smoke_test_id') && (request.params['smoke_test_id'] =~ /[A-Za-z_]+/)
48
+ if query_parameters.key?('smoke_test_id') && (query_parameters['smoke_test_id'] =~ /\A[A-Za-z_]+\z/)
46
49
  # Set HTTP_COOKIE header to enable smoke test on first pageview
47
- cookie = EenyMeeny::Cookie.create_for_smoke_test(request.params['smoke_test_id'])
48
- env = add_http_cookie(env, cookie, precede: true)
50
+ cookie = EenyMeeny::Cookie.create_for_smoke_test(query_parameters['smoke_test_id'])
51
+ env = add_or_replace_http_cookie(env, cookie)
49
52
  new_cookies[cookie.name] = cookie
50
53
  end
51
54
  end
52
- # Clean up 'Set-Cookie' header.
53
- if existing_set_cookie_header.nil?
54
- env.delete('Set-Cookie')
55
- else
56
- env['Set-Cookie'] = existing_set_cookie_header
57
- end
58
55
  # Delegate to app
59
56
  status, headers, body = @app.call(env)
60
57
  response = Rack::Response.new(body, status, headers)
@@ -66,19 +63,22 @@ module EenyMeeny
66
63
  end
67
64
 
68
65
  private
69
- def add_http_cookie(env, cookie, precede: false)
70
- env['Set-Cookie'] = ''
71
- Rack::Utils.set_cookie_header!(env,
72
- cookie.name,
73
- cookie.to_h)
74
- env['HTTP_COOKIE'] = '' if env['HTTP_COOKIE'].nil?
75
- if precede
76
- # Prepend cookie to the 'HTTP_COOKIE' header. This ensures it overwrites existing cookies when present.
77
- env['HTTP_COOKIE'] = env['Set-Cookie'] + '; ' + env['HTTP_COOKIE']
78
- else
79
- env['HTTP_COOKIE'] += '; ' unless env['HTTP_COOKIE'].empty?
80
- env['HTTP_COOKIE'] += env['Set-Cookie']
81
- end
66
+
67
+ def query_hash(env)
68
+ # Query Params are only relevant if EenyMeeny.config have them enabled.
69
+ return {} unless EenyMeeny.config.query_parameters[:experiment] || EenyMeeny.config.query_parameters[:smoke_test]
70
+ # Query Params are only relevant to HTTP GET requests.
71
+ return {} unless env[REQUEST_METHOD] == 'GET'
72
+ Rack::Utils.parse_query(env[QUERY_STRING], '&;')
73
+ end
74
+
75
+ def add_or_replace_http_cookie(env, cookie)
76
+ cookie_name_escaped = Rack::Utils.escape(cookie.name)
77
+ cookie_string = "#{cookie_name_escaped}=#{Rack::Utils.escape(cookie.value)}"
78
+ env[HTTP_COOKIE] = '' if env[HTTP_COOKIE].nil?
79
+ return env if env[HTTP_COOKIE].sub!(/#{Regexp.escape(cookie_name_escaped)}=[^;]+/, cookie_string)
80
+ env[HTTP_COOKIE] += '; ' unless env[HTTP_COOKIE].empty?
81
+ env[HTTP_COOKIE] += cookie_string
82
82
  env
83
83
  end
84
84
  end
@@ -57,12 +57,8 @@ module EenyMeeny
57
57
 
58
58
  def self.read(cookie_string)
59
59
  return if cookie_string.nil? || cookie_string.empty?
60
- begin
61
- return cookie_string unless EenyMeeny.config.secure # Cookie encryption disabled.
62
- EenyMeeny.config.encryptor.decrypt(cookie_string)
63
- rescue
64
- nil # Return nil if cookie is invalid.
65
- end
60
+ return cookie_string unless EenyMeeny.config.secure # Cookie encryption disabled.
61
+ EenyMeeny.config.encryptor.decrypt(cookie_string)
66
62
  end
67
63
 
68
64
  def initialize(name: '', value: '', expires: 1.month.from_now, http_only: true, same_site: nil, path: nil)
@@ -9,11 +9,11 @@ module EenyMeeny
9
9
  initializer 'eeny_meeny.configure' do |app|
10
10
  # Configrue EenyMeeny (defaults set in eeny_meeny.rb)
11
11
  EenyMeeny.configure do |config|
12
- config.cookies = app.config.eeny_meeny[:cookies] if app.config.eeny_meeny.has_key?(:cookies)
13
- config.experiments = app.config.eeny_meeny[:experiments] if app.config.eeny_meeny.has_key?(:experiments)
14
- config.secret = app.config.eeny_meeny[:secret] if app.config.eeny_meeny.has_key?(:secret)
15
- config.secure = app.config.eeny_meeny[:secure] if app.config.eeny_meeny.has_key?(:secure)
16
- config.query_parameters = app.config.eeny_meeny[:query_parameters] if app.config.eeny_meeny.has_key?(:query_parameters)
12
+ config.cookies = app.config.eeny_meeny[:cookies] if app.config.eeny_meeny.key?(:cookies)
13
+ config.experiments = app.config.eeny_meeny[:experiments] if app.config.eeny_meeny.key?(:experiments)
14
+ config.secret = app.config.eeny_meeny[:secret] if app.config.eeny_meeny.key?(:secret)
15
+ config.secure = app.config.eeny_meeny[:secure] if app.config.eeny_meeny.key?(:secure)
16
+ config.query_parameters = app.config.eeny_meeny[:query_parameters] if app.config.eeny_meeny.key?(:query_parameters)
17
17
  end
18
18
  # Include Helpers in ActionController and ActionView
19
19
  ActionController::Base.send :include, EenyMeeny::ExperimentHelper
@@ -1,3 +1,3 @@
1
1
  module EenyMeeny
2
- VERSION = '2.1.1'
2
+ VERSION = '2.1.2'
3
3
  end
@@ -16,7 +16,7 @@ namespace :eeny_meeny do
16
16
 
17
17
  namespace :cookie do
18
18
  desc 'Create a valid EenyMeeny experiment cookie'
19
- task :experiment, [:experiment_id] => :environment do |t, args|
19
+ task :experiment, [:experiment_id] => :environment do |_, args|
20
20
  raise "Missing 'experiment_id' parameter" if (args['experiment_id'].nil? || args['experiment_id'].empty?)
21
21
  experiment_id = args['experiment_id'].to_sym
22
22
  cookie = write_cookie(experiment_id)
@@ -24,7 +24,7 @@ namespace :eeny_meeny do
24
24
  end
25
25
 
26
26
  desc 'Create a valid EenyMeeny experiment cookie for a specific variation'
27
- task :experiment_variation, [:experiment_id, :variation_id] => :environment do |t, args|
27
+ task :experiment_variation, [:experiment_id, :variation_id] => :environment do |_, args|
28
28
  raise "Missing 'experiment_id' parameter" if (args['experiment_id'].nil? || args['experiment_id'].empty?)
29
29
  raise "Missing 'variation_id' parameter" if (args['variation_id'].nil? || args['variation_id'].empty?)
30
30
  experiment_id = args['experiment_id'].to_sym
@@ -34,7 +34,7 @@ namespace :eeny_meeny do
34
34
  end
35
35
 
36
36
  desc 'Create a valid EenyMeeny smoke test cookie'
37
- task :smoke_test, [:smoke_test_id, :version] => :environment do |t, args|
37
+ task :smoke_test, [:smoke_test_id, :version] => :environment do |_, args|
38
38
  raise "Missing 'smoke_test_id' parameter" if (args['smoke_test_id'].nil? || args['smoke_test_id'].empty?)
39
39
  smoke_test_id = args['smoke_test_id']
40
40
  version = args['version'] || 1
@@ -32,13 +32,13 @@ describe EenyMeeny::ExperimentHelper, experiments: true do
32
32
 
33
33
  context 'and given a variation id' do
34
34
  let(:request_with_variation_cookie) do
35
- request.set_cookie(EenyMeeny::Cookie.create_for_experiment_variation(EenyMeeny::Experiment.find_by_id(:my_page), variation_id: :new).to_s)
35
+ request.set_cookie(EenyMeeny::Cookie.create_for_experiment_variation(EenyMeeny::Experiment.find_by_id(:my_page), :new).to_s)
36
36
  request
37
37
  end
38
38
 
39
39
  context 'that matches the variation id the cookie' do
40
40
  it "returns the user's experiment variation" do
41
- allow(subject).to receive(:cookies).and_return(request_with_cookie.cookie_jar)
41
+ allow(subject).to receive(:cookies).and_return(request_with_variation_cookie.cookie_jar)
42
42
  expect(subject.participates_in?(:my_page, variation_id: :new)).to be_a EenyMeeny::Variation
43
43
  expect(subject.participates_in?(:my_page, variation_id: 'new')).to be_a EenyMeeny::Variation
44
44
  end
@@ -46,7 +46,7 @@ describe EenyMeeny::ExperimentHelper, experiments: true do
46
46
 
47
47
  context 'that does not match the variation id the cookie' do
48
48
  it 'returns nil' do
49
- allow(subject).to receive(:cookies).and_return(request_with_cookie.cookie_jar)
49
+ allow(subject).to receive(:cookies).and_return(request_with_variation_cookie.cookie_jar)
50
50
  expect(subject.participates_in?(:my_page, variation_id: :old)).to be_nil
51
51
  expect(subject.participates_in?(:my_page, variation_id: 'old')).to be_nil
52
52
  end
@@ -71,15 +71,15 @@ describe EenyMeeny::Middleware do
71
71
  end
72
72
 
73
73
  context 'and given an experiment query parameter' do
74
- let(:request) { Rack::MockRequest.new(subject) }
74
+ let(:request) { Rack::MockRequest.new(initialize_app(secure: false)) }
75
75
 
76
76
  before(:example) do
77
77
  @response = request.get('/test?eeny_meeny_my_page_v1=old', 'CONTENT_TYPE' => 'text/plain')
78
78
  end
79
79
 
80
80
  it 'selects the correct variation' do
81
- modified_request = Rack::Request.new(app)
82
- expect(EenyMeeny::Cookie.read(modified_request.cookies['eeny_meeny_my_page_v1'])).to eq('old')
81
+ expect(app['HTTP_COOKIE']).to include('eeny_meeny_my_page_v1=old')
82
+ expect(app['HTTP_COOKIE']).to_not include('eeny_meeny_my_page_v1=new')
83
83
  end
84
84
 
85
85
  it "sets the 'HTTP_COOKIE' header on the request" do
@@ -15,6 +15,7 @@ class MockRackApp
15
15
  def [](key)
16
16
  @env[key]
17
17
  end
18
+ alias_method :fetch, :[]
18
19
 
19
20
  def []=(key,value)
20
21
  @env[key] = value
@@ -4,11 +4,10 @@ require 'codeclimate-test-reporter'
4
4
  require 'active_support/time'
5
5
 
6
6
  SimpleCov.start do
7
- formatter SimpleCov::Formatter::MultiFormatter[
7
+ formatter SimpleCov::Formatter::MultiFormatter.new([
8
8
  SimpleCov::Formatter::HTMLFormatter,
9
9
  SimpleCov::Formatter::RcovFormatter,
10
- CodeClimate::TestReporter::Formatter
11
- ]
10
+ CodeClimate::TestReporter::Formatter])
12
11
  add_group('EenyMeeny', 'lib/eeny-meeny')
13
12
  add_group('Rake Tasks', 'lib/tasks')
14
13
  add_group('Specs', 'spec')
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: eeny-meeny
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.1
4
+ version: 2.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Christian Orthmann
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-10-06 00:00:00.000000000 Z
11
+ date: 2017-04-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -140,6 +140,7 @@ executables: []
140
140
  extensions: []
141
141
  extra_rdoc_files: []
142
142
  files:
143
+ - ".travis.yml"
143
144
  - CHANGELOG.md
144
145
  - Gemfile
145
146
  - LICENSE