vignette 0.0.13 → 0.0.14

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: e2899584cc71727faccd169acd629f4823a1dcd8
4
- data.tar.gz: f86dfe90395dbd08d08d426d83854034efb2986c
3
+ metadata.gz: f45e9d2eb38a89e35ec4bf6d0cf62d80f77b92ad
4
+ data.tar.gz: 8ab414f006635373e0a88eaae142b85f4a205c1c
5
5
  SHA512:
6
- metadata.gz: e3edfbbff798f29ef81d164fc8871643f1b74c2b2e649fe7abf939cb28a8348cf022594b5570f8c387fdc8408969eaaa4e85fdf0215612fb3947cd445d966520
7
- data.tar.gz: f364423a9963b72b70ae0ba27b166d74623f216e289b48f69155332625c1483bf536d80984ca101f5fd45269aa201db8012cb206100a673460bd35ca4db8c2c6
6
+ metadata.gz: 0f5b19dea4363391a52e2f7758e03007c0a616d29e15f59f285942d4b15ea3918ff7281160cee2427341bafd0139f7e7c658c97acd97fd13c565a75421c2d183
7
+ data.tar.gz: a98445107d11417e51eef0e16566a5cdc1559995ed061a8cc53b01930e02641ba68ccb96ef088620a10984ed14e60594a9c9e5a65946243975f009f5f38e6823
data/README.md CHANGED
@@ -64,16 +64,6 @@ The choices for these tests are exposed through the `Vignette.test` method:
64
64
 
65
65
  You may store these values as properties in your analytics system.
66
66
 
67
- ## Caveats
68
-
69
- Tests are stored in your store object based on the original input. If you change the input of the test (regardless of the name), a new test will be created. Thus [1,2,3].vignette(:numbers) and [2,3,4].vignette(:numbers) will always be considered unique tests. New tests will overwrite old tests with the same name in `Vignette.tests`. This is by design so that you can update the test, have new results, but keep the same test name in your analytics system.
70
-
71
- Note, you must use arrays that will not change between runs. Thus, `[Date.today, 1.days.ago].vignette` will end up creating separate tests every time this code is run. We use a `Zlib.crc32` check to check for uniqueness of an array. Instead, this should be `[0,1].vignette.days.ago`.
72
-
73
- If you choose to store your `tests` in `cookies`, then the chosen result will be stored in a cookie sent to the user's browser. Thus, be careful not to store any secret information in a test.
74
-
75
- For unnamed tests, the system will try to determine the name from the line in the file (e.g. `(app/models/user.rb:50)`). When it does this, it will keep the name the same so long as the underlying array has the same crc32 code. That is, even if that vignette moves off of line 50, the test will keep the same "name" until the choices of the test change. This is a trade-off to allow un-named vignettes to be easily understood in an analytics system without having to name each one unless you would like to.
76
-
77
67
  ## Naming Tests
78
68
 
79
69
  Vignette tests can also be specifically named, e.g:
@@ -92,6 +82,16 @@ This will lead to `Vignette.tests` to include: `{ "cat_names" => "Chairman Meow"
92
82
 
93
83
  Without a name, Vignettes will try to name themselves based on the name of the falling calling them, e.g. `(app/models/user.rb:25)` or `(app/views/users/new.html.haml:22)`
94
84
 
85
+ ## Caveats
86
+
87
+ Tests are stored in your store object based on the original input. If you change the input of the test (regardless of the name), a new test will be created. Thus [1,2,3].vignette(:numbers) and [2,3,4].vignette(:numbers) will always be considered unique tests. New tests will overwrite old tests with the same name in `Vignette.tests`. This is by design so that you can update the test, have new results, but keep the same test name in your analytics system.
88
+
89
+ Note, you must use arrays that will not change between runs. Thus, `[Date.today, 1.days.ago].vignette` will end up creating separate tests every time this code is run. We use a `Zlib.crc32` check to check for uniqueness of an array. Instead, this should be `[0,1].vignette.days.ago`.
90
+
91
+ If you choose to store your `tests` in `cookies`, then the chosen result will be stored in a cookie sent to the user's browser. Thus, be careful not to store any secret information in a test.
92
+
93
+ For unnamed tests, the system will try to determine the name from the line in the file (e.g. `(app/models/user.rb:50)`). When it does this, it will keep the name the same so long as the underlying array has the same crc32 code. That is, even if that vignette moves off of line 50, the test will keep the same "name" until the choices of the test change. This is a trade-off to allow un-named vignettes to be easily understood in an analytics system without having to name each one unless you would like to.
94
+
95
95
  ## Contributing
96
96
 
97
97
  1. Fork it
@@ -20,13 +20,12 @@ module ObjectExtensions
20
20
  key = "vignette_#{vignette_crc}"
21
21
  test_name = nil
22
22
 
23
- store = Vignette.get_store
24
- v = store[:v] ? JSON(store[:v]) : {}
23
+ vig = Vignette.vig
25
24
 
26
25
  test_name = if expect_consistent_name && name.present?
27
26
  name
28
- elsif v[vignette_crc]
29
- v[vignette_crc]['n']
27
+ elsif vig[vignette_crc]
28
+ vig[vignette_crc]['n']
30
29
  elsif name.present? # this logic looks weird, but this is if we don't expect consistent names *and* we don't have a name in v[]
31
30
  name
32
31
  else
@@ -34,13 +33,13 @@ module ObjectExtensions
34
33
  "(#{Vignette::strip_path(loc.absolute_path)}:#{loc.lineno})"
35
34
  end
36
35
 
37
- result = if v.has_key?(vignette_crc)
38
- v[vignette_crc]['v']
36
+ result = if vig.has_key?(vignette_crc)
37
+ vig[vignette_crc]['v']
39
38
  else
40
39
  # Store key into storage if not available
41
40
  new_value = self[Kernel.rand(length)]
42
41
 
43
- store[:v] = v.merge(vignette_crc => { n: test_name, v: new_value, t: Time.now.to_i }).to_json
42
+ Vignette.set_vig( vig.merge(vignette_crc => { n: test_name, v: new_value, t: Time.now.to_i }) )
44
43
 
45
44
  new_value
46
45
  end
@@ -53,16 +52,25 @@ module ObjectExtensions
53
52
  module ActionControllerExtensions
54
53
 
55
54
  def self.included(controller)
56
- controller.around_filter(:init_vignette)
55
+ controller.around_filter(:with_vignettes)
57
56
  end
58
57
 
59
58
  private
60
59
 
61
- def init_vignette
62
- Vignette.request_config(request, session, cookies)
63
- yield
64
- ensure
65
- Vignette.clear_request # Clear request
60
+ def with_vignettes
61
+ # set repo based on what type of store we want
62
+ repo = case Vignette.store
63
+ when :session
64
+ session
65
+ when :cookies
66
+ cookies
67
+ when nil, :random
68
+ Hash.new
69
+ end
70
+
71
+ Vignette.with_repo(repo) do
72
+ yield
73
+ end
66
74
  end
67
75
 
68
76
  end
@@ -1,3 +1,3 @@
1
1
  module Vignette
2
- VERSION = "0.0.13"
2
+ VERSION = "0.0.14"
3
3
  end
data/lib/vignette.rb CHANGED
@@ -13,10 +13,9 @@ module Vignette
13
13
  class TemplateRequiresNameError < VignetteStandardError; end
14
14
  end
15
15
 
16
- # Your code goes here...
16
+ # Module Attributes, please set via `init()`
17
17
  mattr_accessor :logging
18
18
  mattr_accessor :store
19
- mattr_accessor :request, :session, :cookies
20
19
 
21
20
  # Initialization Code
22
21
 
@@ -30,66 +29,67 @@ module Vignette
30
29
 
31
30
  # Member Functions
32
31
 
32
+ # Set any initializers
33
33
  def self.init(opts={})
34
- opts = {
35
- store: nil,
36
- logging: nil
37
- }.with_indifferent_access.merge(opts)
34
+ opts.each do |k,v|
35
+ Vignette.send("#{k}=", v)
36
+ end
37
+ end
38
38
 
39
- Vignette.store = opts[:store]
39
+ # Sets the current repo to be used to get and store tests for this thread
40
+ def self.set_repo(repo)
41
+ Thread.current[:vignette_repo] = repo
40
42
  end
41
-
42
- # Settings for configuations
43
- def self.request_config(request, session, cookies)
44
- Vignette.request = request
45
- Vignette.session = session
46
- Vignette.cookies = cookies
43
+
44
+ # Clears the current repo on this thread
45
+ def self.clear_repo
46
+ set_repo(nil)
47
47
  end
48
48
 
49
- def self.with_settings(request, session, cookies)
49
+ # Performs block with repo set to `repo` for this thread
50
+ def self.with_repo(repo)
50
51
  begin
51
- Vignette.request_config(request, session, cookies)
52
+ Vignette.set_repo(repo)
52
53
 
53
54
  yield
54
55
  ensure
55
- Vignette.clear_request
56
+ Vignette.clear_repo
56
57
  end
57
58
  end
58
-
59
- def self.clear_request
60
- Vignette.request = Vignette.session = Vignette.cookies = nil # clear items
59
+
60
+ # Is Vignette active for this thread (i.e. do we have a repo?)
61
+ def self.active?
62
+ !Thread.current[:vignette_repo].nil?
61
63
  end
62
-
63
- def self.tests(session=Vignette.session, cookies=Vignette.cookies)
64
- store = get_store(session, cookies)
65
- store && store[:v].present? ? JSON(store[:v]) : {}
66
- if store && store[:v].present?
67
- v = JSON(store[:v])
68
64
 
69
- name_values = v.values.map { |v| [ v['n'], [ v['t'], v['v'] ] ] }.group_by { |el| el[0] }
65
+ # Get the repo for this thread
66
+ def self.repo
67
+ raise Errors::ConfigError.new("Repo not active, please call Vignette.set_repo before using Vignette (or use around_filter in Rails)") if !active?
70
68
 
71
- arr = name_values.map { |k,v| [ k.to_sym, v.sort { |a,b| b[1][0] <=> a[1][0] }.first[1][1] ] }
69
+ Thread.current[:vignette_repo]
70
+ end
72
71
 
73
- Hash[arr]
74
- else
75
- {}
76
- end
72
+ # From the repo (default whatever is set for the thread), grab Vignettes' repo and unpack
73
+ def self.vig(repo=nil)
74
+ repo ||= Vignette.repo # allow using existing
75
+
76
+ repo && repo[:v].present? ? JSON(repo[:v]) : {}
77
77
  end
78
-
79
- def self.get_store(session=Vignette.session, cookies=Vignette.cookies)
80
- case Vignette.store
81
- when :cookies
82
- raise Errors::ConfigError, "Missing cookies configuration in Vignette. Must access Vignette in controller within around_filter." if cookies.nil?
83
- Rails.logger.debug [ 'Vignette::vignette', 'Cookies Sampling', cookies ] if Vignette.logging
84
- cookies.signed
85
- when :session
86
- raise Errors::ConfigError, "Missing session configuration in Vignette. Must access Vignette in controller within around_filter." if session.nil?
87
- Rails.logger.debug [ 'Vignette::vignette', 'Session Sampling', session ] if Vignette.logging
88
- session
89
- else
90
- Rails.logger.debug [ 'Vignette::vignette', 'Random Sampling' ] if Vignette.logging
91
- {} # This is an empty storage
92
- end
78
+
79
+ # For this repo, store an update Vig
80
+ def self.set_vig(vig)
81
+ repo[:v] = vig.to_json
82
+ end
83
+
84
+ # Pull all the tests for this current repo
85
+ def self.tests(vig=nil)
86
+ vig ||= Vignette.vig
87
+
88
+ name_values = vig.values.map { |v| [ v['n'], [ v['t'], v['v'] ] ] }.group_by { |el| el[0] }
89
+
90
+ arr = name_values.map { |k,v| [ k.to_sym, v.sort { |a,b| b[1][0] <=> a[1][0] }.first[1][1] ] }
91
+
92
+ Hash[arr]
93
93
  end
94
94
 
95
95
  private
@@ -17,7 +17,7 @@ describe Haml::Filters::Vignette do
17
17
  " }
18
18
 
19
19
  it "should raise error" do
20
- Vignette.with_settings(nil, session, nil) do
20
+ Vignette.with_repo(session) do
21
21
  html = Haml::Engine.new(template).render
22
22
 
23
23
  expect(html).to match(/\<p\>\s+three\s+\<\/p\>\n/)
@@ -38,7 +38,7 @@ describe Haml::Filters::Vignette do
38
38
  " }
39
39
 
40
40
  it "should correctly call vignette from render" do
41
- Vignette.with_settings(nil, session, nil) do
41
+ Vignette.with_repo(session) do
42
42
  html = Haml::Engine.new(template).render
43
43
 
44
44
  expect(html).to eq("<p>\n three\n</p>\n")
@@ -53,7 +53,7 @@ describe Haml::Filters::Vignette do
53
53
  before(:each) { expect(Kernel).to receive(:rand).and_return(2, 1) }
54
54
 
55
55
  it "should correctly call vignette from render" do
56
- Vignette.with_settings(nil, session, nil) do
56
+ Vignette.with_repo(session) do
57
57
  template = File.read(template_file)
58
58
 
59
59
  html = Haml::Engine.new(template, filename: template_file).render
@@ -2,6 +2,47 @@ require 'spec_helper'
2
2
 
3
3
  describe ObjectExtensions do
4
4
 
5
- pending "Add spec tests for object extensions" # note, these are mostly in `vignette_spec`
5
+ context "with a test controller" do
6
+ class TestController
7
+ @@filters = []
6
8
 
9
+ def self.filters
10
+ @@filters
11
+ end
12
+
13
+ def self.around_filter(filter)
14
+ @@filters << filter
15
+ end
16
+
17
+ include ObjectExtensions::ActionControllerExtensions
18
+
19
+ def session
20
+ { session_id: "whatever" }
21
+ end
22
+
23
+ def run(&block)
24
+ filter = @@filters.first # just make this easy
25
+
26
+ self.send(filter, &block)
27
+ end
28
+ end
29
+
30
+ it "should register an around_filter" do
31
+ expect(TestController.filters.count).to eq(1)
32
+ end
33
+
34
+ it "should correctly run around_filter" do
35
+ expect(Kernel).to receive(:rand).and_return(2)
36
+ expect(Vignette.active?).to be(false)
37
+
38
+ tc = TestController.new
39
+
40
+ tc.run do
41
+ expect(Vignette.repo).to eq(tc.session)
42
+ expect(Vignette.active?).to be(true)
43
+ expect([1,2,3].vignette(:number)).to eq(3)
44
+ expect(Vignette.tests).to eq(number: 3)
45
+ end
46
+ end
47
+ end
7
48
  end
@@ -5,7 +5,7 @@ describe Vignette do
5
5
  context 'with simple session store' do
6
6
  let!(:session) { Hash.new }
7
7
  let(:array) { %w{a b c} }
8
- before { Vignette.session = session }
8
+ before { Vignette.set_repo(session) }
9
9
 
10
10
  it 'should have correct crc' do
11
11
  expect(array.crc).to eq(891568578)
@@ -98,4 +98,83 @@ describe Vignette do
98
98
  end
99
99
  end
100
100
 
101
+ context 'when threading' do
102
+ before(:each) { expect(Kernel).to receive(:rand).and_return(1, 2) }
103
+
104
+ context 'serially' do
105
+ it 'should work like normal' do
106
+
107
+ Thread.new do
108
+ Vignette.with_repo(Hash.new) do
109
+ expect(%w{a b c}.vignette(:name)).to eq('b')
110
+ expect(Vignette.tests).to eq(name: 'b')
111
+ end
112
+ end.join
113
+
114
+ Thread.new do
115
+ Vignette.with_repo(Hash.new) do
116
+ expect(%w{a b c}.vignette(:name)).to eq('c')
117
+ expect(Vignette.tests).to eq(name: 'c')
118
+ end
119
+ end.join
120
+ end
121
+
122
+ end
123
+
124
+ context 'with no delays' do
125
+ it 'should work like normal' do
126
+ threads = []
127
+
128
+ threads << Thread.new do
129
+ Vignette.with_repo(Hash.new) do
130
+ expect(%w{a b c}.vignette(:name)).to eq('b')
131
+ expect(Vignette.tests).to eq(name: 'b')
132
+ end
133
+ end
134
+
135
+ threads << Thread.new do
136
+ Vignette.with_repo(Hash.new) do
137
+ expect(%w{a b c}.vignette(:name)).to eq('c')
138
+ expect(Vignette.tests).to eq(name: 'c')
139
+ end
140
+ end
141
+
142
+ threads.each(&:join)
143
+ end
144
+ end
145
+
146
+ context 'with a one second delay' do
147
+ it 'should work like normal' do
148
+ threads = []
149
+
150
+ threads << Thread.new do
151
+ Vignette.with_repo(Hash.new) do
152
+ sleep 0.1 # this is to cause a race condition
153
+ expect(%w{a b c}.vignette(:name)).to eq('c')
154
+ expect(Vignette.tests).to eq(name: 'c')
155
+
156
+ sleep 0.2
157
+ expect(%w{a b c}.vignette(:name)).to eq('c')
158
+ expect(Vignette.tests).to eq(name: 'c')
159
+ end
160
+ end
161
+
162
+ threads << Thread.new do
163
+ Vignette.with_repo(Hash.new) do
164
+ expect(%w{a b c}.vignette(:name)).to eq('b')
165
+ expect(Vignette.tests).to eq(name: 'b')
166
+
167
+ sleep 0.2
168
+
169
+ expect(%w{a b c}.vignette(:name)).to eq('b')
170
+ expect(Vignette.tests).to eq(name: 'b')
171
+ end
172
+ end
173
+
174
+ threads.each(&:join)
175
+ end
176
+ end
177
+
178
+ end
179
+
101
180
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: vignette
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.13
4
+ version: 0.0.14
5
5
  platform: ruby
6
6
  authors:
7
7
  - Geoff Hayes