eeny-meeny 1.0.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  require 'spec_helper'
2
- require 'eeny-meeny/variation'
2
+ require 'eeny-meeny/models/variation'
3
3
 
4
4
  describe EenyMeeny::Variation do
5
5
  describe 'when initialized' do
@@ -0,0 +1,39 @@
1
+ require 'eeny-meeny/routing/experiment_constraint'
2
+ require 'eeny-meeny/middleware'
3
+ require 'rack/test'
4
+
5
+ describe EenyMeeny::ExperimentConstraint, experiments: true do
6
+
7
+ let(:request) do
8
+ session = Rack::MockSession.new(EenyMeeny::Middleware.new(MockRackApp.new))
9
+ session.set_cookie('eeny_meeny_my_page_v1=IlI%2FGW9IZvayAGQbBOroxIrfr6Z116OJqdjFdrw6FOZXOrinmxQmsKw2a%2Fb8kJFP0Up%2BLr4FACovT9%2Bo0hRdcY0AJtcYqMXC96GDMSwa2HauZbjHw16Q3%2BboSnWjfaEOHmqlyxtPxQwxlr3rsT%2FYblPjqqQ%2FiPbaJUqou3LiMtpVg4V%2FJxJdhn0XJUgFMDaFWXVFYYA6VmJSFUGglhRlbg%3D%3D; path=/; expires=Tue, 11 Oct 2016 13:07:53 -0000; HttpOnly')
10
+ session
11
+ end
12
+
13
+ describe 'when initialized' do
14
+
15
+ context 'for an inactive experiment' do
16
+ subject do
17
+ described_class.new(:expired)
18
+ end
19
+
20
+ describe '#matches?' do
21
+ it 'returns false' do
22
+ expect(subject.matches?(request)).to be false
23
+ end
24
+ end
25
+ end
26
+
27
+ context 'for an active experiment' do
28
+ subject do
29
+ described_class.new(:my_page)
30
+ end
31
+
32
+ describe '#matches?' do
33
+ it 'returns true' do
34
+ expect(subject.matches?(request)).to be true
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,35 @@
1
+ require 'eeny-meeny/routing/smoke_test_constraint'
2
+ require 'eeny-meeny/middleware'
3
+ require 'rack/test'
4
+
5
+ describe EenyMeeny::SmokeTestConstraint do
6
+
7
+ let(:request) do
8
+ Rack::MockSession.new(EenyMeeny::Middleware.new(MockRackApp.new))
9
+ end
10
+
11
+ let(:request_with_cookie) do
12
+ request.set_cookie('smoke_test_shadow_v1=kqe%2Bt%2F72JZ9s7fOv0nQ8GszTEmmXj3tUsjqmqy31i4yZLku5okuya%2F3PYb8Oi%2BSi53hDP8egfeiCcbrlBN4s5ifQwToaZHNAs43V1EKb8ca%2BTRK0lpCWfR58%2BQjpWwZL; expires=Tue, 11 Oct 2016 13:30:31 -0000; HttpOnly')
13
+ request
14
+ end
15
+
16
+ describe 'when initialized' do
17
+
18
+ subject do
19
+ described_class.new(:shadow)
20
+ end
21
+
22
+ describe '#matches?' do
23
+ context 'for a request with a valid smoke test cookie' do
24
+ it 'returns true' do
25
+ expect(subject.matches?(request_with_cookie)).to be true
26
+ end
27
+ end
28
+ context 'for a request without a smoke test cookie' do
29
+ it 'returns false' do
30
+ expect(subject.matches?(request)).to be false
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -8,3 +8,15 @@
8
8
  :new:
9
9
  :name: New My Page
10
10
  :weight: 0.98
11
+ :expired:
12
+ :name: Expired
13
+ :version: 1
14
+ :start_at: '2016-08-11T11:55:40Z'
15
+ :end_at: '2016-08-11T11:55:40Z'
16
+ :variations:
17
+ :old:
18
+ :name: Old Expired Page
19
+ :weight: 0.02
20
+ :new:
21
+ :name: New Expired Page
22
+ :weight: 0.98
data/spec/spec_helper.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  require 'simplecov'
2
2
  require 'simplecov-rcov'
3
3
  require 'codeclimate-test-reporter'
4
+ require 'active_support/time'
4
5
 
5
6
  SimpleCov.start do
6
7
  formatter SimpleCov::Formatter::MultiFormatter[
@@ -9,16 +10,32 @@ SimpleCov.start do
9
10
  CodeClimate::TestReporter::Formatter
10
11
  ]
11
12
  add_group('EenyMeeny', 'lib/eeny-meeny')
13
+ add_group('Rake Tasks', 'lib/tasks')
12
14
  add_group('Specs', 'spec')
13
15
  end
14
16
 
15
17
  require 'rspec'
16
18
  require 'yaml'
17
- require 'eeny-meeny'
18
19
  require 'mock_rack_app'
19
20
 
21
+ require 'eeny-meeny'
22
+
20
23
  RSpec.configure do |config|
21
24
  config.run_all_when_everything_filtered = true
22
25
  config.filter_run :focus
23
26
  config.order = "random"
27
+
28
+ config.before(:suite) do
29
+ Time.zone = 'UTC'
30
+ end
31
+
32
+ config.before(:each) do
33
+ EenyMeeny.reset! # reset configuration before every test.
34
+ end
35
+ config.before(:each, experiments: true) do
36
+ EenyMeeny.configure do |config|
37
+ config.experiments = YAML.load_file(File.join('spec','fixtures','experiments.yml'))
38
+ end
39
+ end
40
+
24
41
  end
@@ -0,0 +1,72 @@
1
+ require 'spec_helper'
2
+ require 'rake'
3
+
4
+ describe 'cookie.rake', experiments: true do
5
+ before do
6
+ Rake.application.rake_require "tasks/cookie"
7
+ Rake::Task.define_task(:environment)
8
+ end
9
+
10
+ describe 'eeny_meeny:cookie:experiment' do
11
+ context 'executed with an experiment id' do
12
+ it 'generates a cookie' do
13
+ expect {
14
+ Rake::Task['eeny_meeny:cookie:experiment'].execute(Rake::TaskArguments.new([:experiment_id],['my_page']))
15
+ }.to_not raise_error
16
+ end
17
+ end
18
+
19
+ context 'executed without arguments' do
20
+ it 'results in an error' do
21
+ expect {
22
+ Rake::Task['eeny_meeny:cookie:experiment'].execute
23
+ }.to raise_error(RuntimeError, "Missing 'experiment_id' parameter")
24
+ end
25
+ end
26
+ end
27
+
28
+ describe 'eeny_meeny:cookie:experiment_variation' do
29
+ context 'executed with an experiment id' do
30
+ it 'results in an error' do
31
+ expect {
32
+ Rake::Task['eeny_meeny:cookie:experiment_variation'].execute(Rake::TaskArguments.new([:experiment_id],['my_page']))
33
+ }.to raise_error(RuntimeError, "Missing 'variation_id' parameter")
34
+ end
35
+
36
+ context 'and a variation_id' do
37
+ it 'generates a cookie' do
38
+ expect {
39
+ Rake::Task['eeny_meeny:cookie:experiment_variation'].execute(Rake::TaskArguments.new([:experiment_id, :variation_id],['my_page', 'new']))
40
+ }.to_not raise_error
41
+ end
42
+ end
43
+ end
44
+
45
+ context 'executed without arguments' do
46
+ it 'results in an error' do
47
+ expect {
48
+ Rake::Task['eeny_meeny:cookie:experiment_variation'].execute
49
+ }.to raise_error(RuntimeError, "Missing 'experiment_id' parameter")
50
+ end
51
+ end
52
+ end
53
+
54
+ describe 'eeny_meeny:cookie:smoke_test' do
55
+ context 'executed with an smoke test id' do
56
+ it 'generates a cookie' do
57
+ expect {
58
+ Rake::Task['eeny_meeny:cookie:smoke_test'].execute(Rake::TaskArguments.new([:smoke_test_id],['shadow']))
59
+ }.to_not raise_error
60
+ end
61
+ end
62
+
63
+ context 'executed without arguments' do
64
+ it 'results in an error' do
65
+ expect {
66
+ Rake::Task['eeny_meeny:cookie:smoke_test'].execute
67
+ }.to raise_error(RuntimeError, "Missing 'smoke_test_id' parameter")
68
+ end
69
+ end
70
+ end
71
+
72
+ end
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: 1.0.0
4
+ version: 2.0.0
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-07-03 00:00:00.000000000 Z
11
+ date: 2016-09-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -80,6 +80,20 @@ dependencies:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rack-test
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: 0.6.3
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: 0.6.3
83
97
  - !ruby/object:Gem::Dependency
84
98
  name: rack
85
99
  requirement: !ruby/object:Gem::Requirement
@@ -133,22 +147,27 @@ files:
133
147
  - Rakefile
134
148
  - eeny-meeny.gemspec
135
149
  - lib/eeny-meeny.rb
136
- - lib/eeny-meeny/encryptor.rb
137
- - lib/eeny-meeny/experiment.rb
138
150
  - lib/eeny-meeny/experiment_helper.rb
139
151
  - lib/eeny-meeny/middleware.rb
140
- - lib/eeny-meeny/middleware_helper.rb
152
+ - lib/eeny-meeny/models/cookie.rb
153
+ - lib/eeny-meeny/models/encryptor.rb
154
+ - lib/eeny-meeny/models/experiment.rb
155
+ - lib/eeny-meeny/models/variation.rb
141
156
  - lib/eeny-meeny/railtie.rb
142
- - lib/eeny-meeny/route_constraint.rb
143
- - lib/eeny-meeny/shared_methods.rb
144
- - lib/eeny-meeny/variation.rb
157
+ - lib/eeny-meeny/routing/experiment_constraint.rb
158
+ - lib/eeny-meeny/routing/smoke_test_constraint.rb
145
159
  - lib/eeny-meeny/version.rb
146
- - spec/eeny-meeny/experiment_spec.rb
160
+ - lib/tasks/cookie.rake
147
161
  - spec/eeny-meeny/middleware_spec.rb
148
- - spec/eeny-meeny/variation_spec.rb
162
+ - spec/eeny-meeny/models/cookie_spec.rb
163
+ - spec/eeny-meeny/models/experiment_spec.rb
164
+ - spec/eeny-meeny/models/variation_spec.rb
165
+ - spec/eeny-meeny/routing/experiment_constraint_spec.rb
166
+ - spec/eeny-meeny/routing/smoke_test_constraint_spec.rb
149
167
  - spec/fixtures/experiments.yml
150
168
  - spec/mock_rack_app.rb
151
169
  - spec/spec_helper.rb
170
+ - spec/tasks/cookie_task_spec.rb
152
171
  homepage: http://rubygems.org/gems/eeny-meeny
153
172
  licenses:
154
173
  - MIT
@@ -169,9 +188,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
169
188
  version: '0'
170
189
  requirements: []
171
190
  rubyforge_project:
172
- rubygems_version: 2.4.8
191
+ rubygems_version: 2.5.1
173
192
  signing_key:
174
193
  specification_version: 4
175
- summary: A simple split testing tool for Rails
194
+ summary: A simple split and smoke testing tool for Rails
176
195
  test_files: []
177
- has_rdoc:
@@ -1,25 +0,0 @@
1
- module EenyMeeny::MiddlewareHelper
2
- def has_experiment_cookie?(cookies, experiment)
3
- cookies.has_key?(experiment_cookie_name(experiment))
4
- end
5
-
6
- def generate_cookie_value(experiment, cookie_config)
7
- variation = experiment.pick_variation
8
- cookie = {
9
- expires: (experiment.end_at || 1.year.from_now),
10
- httponly: true,
11
- value: Marshal.dump({
12
- name: experiment.name,
13
- variation: variation,
14
- })
15
- }
16
- cookie[:same_site] = cookie_config[:same_site] unless cookie_config[:same_site].nil?
17
- cookie[:path] = cookie_config[:path] unless cookie_config[:path].nil?
18
- cookie
19
- end
20
-
21
- private
22
- def experiment_cookie_name(experiment)
23
- EenyMeeny::EENY_MEENY_COOKIE_PREFIX+experiment.id.to_s+'_v'+experiment.version.to_s
24
- end
25
- end
@@ -1,33 +0,0 @@
1
- require 'eeny-meeny/shared_methods'
2
-
3
- module EenyMeeny
4
- class RouteConstraint
5
- @@eeny_meeny_encryptor = nil
6
-
7
- def initialize(experiment_id, variation_id: nil)
8
- @experiment_id = experiment_id
9
- @variation_id = variation_id
10
- @version = experiment_version(experiment_id)
11
- end
12
-
13
- def matches?(request)
14
- !participates_in?(request).nil?
15
- end
16
-
17
- private
18
-
19
- def participates_in?(request)
20
- cookie = eeny_meeny_cookie(request)
21
- cookie[:variation] unless cookie.nil? || (!cookie.nil? && @variation_id.present? && @variation_id != cookie[:variation].id)
22
- end
23
-
24
- def eeny_meeny_cookie(request)
25
- cookie = request.cookie_jar[EenyMeeny::EENY_MEENY_COOKIE_PREFIX+@experiment_id.to_s+'_v'+@version.to_s]
26
- if cookie
27
- Marshal.load(decrypt(cookie)) rescue nil
28
- end
29
- end
30
-
31
- include EenyMeeny::SharedMethods
32
- end
33
- end
@@ -1,23 +0,0 @@
1
- module EenyMeeny::SharedMethods
2
-
3
- private
4
-
5
- def experiment_version(experiment_id)
6
- (Rails.application.config.eeny_meeny.experiments.
7
- try(:[], experiment_id.to_sym).try(:[], :version) || 1) rescue 1
8
- end
9
-
10
- def decrypt(cookie)
11
- begin
12
- if Rails.application.config.eeny_meeny.secure
13
- # Memoize encryptor to avoid creating new instances on every request.
14
- @@eeny_meeny_encryptor ||= EenyMeeny::Encryptor.new(Rails.application.config.eeny_meeny.secret)
15
- @@eeny_meeny_encryptor.decrypt(cookie)
16
- else
17
- cookie
18
- end
19
- rescue
20
- nil
21
- end
22
- end
23
- end
@@ -1,62 +0,0 @@
1
- require 'spec_helper'
2
- require 'eeny-meeny/experiment'
3
- require 'eeny-meeny/variation'
4
-
5
- describe EenyMeeny::Experiment do
6
- describe 'when initialized' do
7
-
8
- context 'with weighted variations' do
9
- subject do
10
- described_class.new(:experiment_1,
11
- name: 'Test 1',
12
- variations: {
13
- a: { name: 'A', weight: 0.5 },
14
- b: { name: 'B', weight: 0.3 }})
15
- end
16
-
17
- it 'sets the instance variables' do
18
- expect(subject.id).to eq(:experiment_1)
19
- expect(subject.name).to eq('Test 1')
20
- expect(subject.variations).to be_a Array
21
- expect(subject.variations.size).to eq(2)
22
- end
23
-
24
- it "has a 'total_weight' equal to the sum of the variation weights" do
25
- expect(subject.total_weight).to eq(0.8)
26
- end
27
-
28
- describe '#pick_variation' do
29
- it 'picks a variation' do
30
- expect(subject.pick_variation).to be_a EenyMeeny::Variation
31
- end
32
- end
33
- end
34
-
35
- context 'with non-weighted variations' do
36
- subject do
37
- described_class.new(:experiment_1,
38
- name: 'Test 1',
39
- variations: {
40
- a: { name: 'A' },
41
- b: { name: 'B' }})
42
- end
43
-
44
- it 'sets the instance variables' do
45
- expect(subject.id).to eq(:experiment_1)
46
- expect(subject.name).to eq('Test 1')
47
- expect(subject.variations).to be_a Array
48
- expect(subject.variations.size).to eq(2)
49
- end
50
-
51
- it "has a 'total_weight' equal to the number of the variation weights" do
52
- expect(subject.total_weight).to eq(2)
53
- end
54
-
55
- describe '#pick_variation' do
56
- it 'picks a variation' do
57
- expect(subject.pick_variation).to be_a EenyMeeny::Variation
58
- end
59
- end
60
- end
61
- end
62
- end