vanity 2.2.8 → 2.2.9

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: 32b2be75e4a14bf3ce84ba1a49f3ae8b86e6fcf6
4
- data.tar.gz: 40c8689393515ddd853df28130eace503b1327cb
3
+ metadata.gz: ca984620557d326a44e57c76e179701e43c784aa
4
+ data.tar.gz: 3a27cad8226fbdf3e56709291b1ed2afc1793acd
5
5
  SHA512:
6
- metadata.gz: 91931eacb3c34fdf4e9caa8c1ca98b62f0e3cbfd521a58ace103eec63177d28fcfe64646db79d25bb1daeac93dda0814c02dc8d665d2509ccaed30a00c230493
7
- data.tar.gz: 1604cd40d0e73ac1e26a0ba5eebbeaba98e5efa03e23bb50dde6d279325302e8881852808470c3b170b98345d6f36f6d457fdebd4f578404c7b5620f3a1650fb
6
+ metadata.gz: 1e3a77b2540e8726b77bc6f5ec80226f39b7f499abd271d2e5bc20c51131998472e0dffa0879cdf799e1a4de7e7b6cc052e1ea2e87b85fc09bb584df21e8293c
7
+ data.tar.gz: 761fb69735eb0b02e8267a24243bb7cf923ccd8880a14ed691ae55f207aa73c4bed6e5c2566e6179aa264c6defacf519462db3c8106830e5a1432f53607d590c
data/CHANGELOG CHANGED
@@ -1,5 +1,9 @@
1
1
  == Unreleased
2
2
 
3
+ == 2.2.9 (2018-02-03)
4
+
5
+ * Fix race condition using the activerecord adapater when vanity creates experiments or rows in the conversions table (@fcheung)
6
+
3
7
  == 2.2.8 (2017-09-26)
4
8
  * Rails 5.1 compatibility (#326, #327, #330, @sebjacobs, @bensheldon, @terracatta)
5
9
 
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- vanity (2.2.8)
4
+ vanity (2.2.9)
5
5
  i18n
6
6
 
7
7
  GEM
data/README.md CHANGED
@@ -9,13 +9,34 @@ Vanity is an A/B testing framework for Rails that is datastore agnostic.
9
9
 
10
10
  [![Dashboard](doc/images/sidebar_test.png)](http://github.com/assaf/vanity)
11
11
 
12
- ## A/B Testing With Rails
13
-
14
- ### **Step 1:** Start using Vanity in your Rails application
15
-
16
- #### Step 1.1
17
-
18
- ##### Rails 3 & Rails 4 installation
12
+ <!-- toc -->
13
+
14
+ - [Installation](#installation)
15
+ - [Setup](#setup)
16
+ * [Datastore](#datastore)
17
+ + [Redis Setup](#redis-setup)
18
+ + [MongoDB Setup](#mongodb-setup)
19
+ + [SQL Database Setup](#sql-database-setup)
20
+ + [Forking servers and reconnecting](#forking-servers-and-reconnecting)
21
+ * [Initialization](#initialization)
22
+ * [User identification](#user-identification)
23
+ + [Rails](#rails)
24
+ + [Other](#other)
25
+ * [Define a A/B test](#define-a-ab-test)
26
+ * [Present the different options to your users](#present-the-different-options-to-your-users)
27
+ * [Measure conversion](#measure-conversion)
28
+ * [Check the report](#check-the-report)
29
+ + [Rails report dashboard](#rails-report-dashboard)
30
+ - [Registering participants with Javascript](#registering-participants-with-javascript)
31
+ - [Compatibility](#compatibility)
32
+ - [Testing](#testing)
33
+ - [Updating documentation](#updating-documentation)
34
+ - [Contributing](#contributing)
35
+ - [Credits/License](#creditslicense)
36
+
37
+ <!-- tocstop -->
38
+
39
+ ## Installation
19
40
 
20
41
  Add to your Gemfile:
21
42
 
@@ -26,14 +47,16 @@ gem "vanity"
26
47
  (For support for older versions of Rails and Ruby 1.8, please see the [1.9.x
27
48
  branch](https://github.com/assaf/vanity/tree/1-9-stable).)
28
49
 
29
- #### Step 1.2
50
+ ## Setup
51
+
52
+ ### Datastore
30
53
 
31
54
  Choose a datastore that best fits your needs and preferences for storing
32
55
  experiment results. Choose one of: Redis, MongoDB or an SQL database. While
33
56
  Redis is usually faster, it may add additional complexity to your stack.
34
57
  Datastores should be configured using a `config/vanity.yml`.
35
58
 
36
- ##### Redis Setup
59
+ #### Redis Setup
37
60
 
38
61
  Add to your Gemfile:
39
62
 
@@ -73,7 +96,7 @@ Vanity.connect!(
73
96
  )
74
97
  ```
75
98
 
76
- ##### MongoDB Setup
99
+ #### MongoDB Setup
77
100
 
78
101
  Add to your Gemfile:
79
102
 
@@ -94,7 +117,7 @@ production:
94
117
  database: analytics
95
118
  ```
96
119
 
97
- ##### SQL Database Setup
120
+ #### SQL Database Setup
98
121
 
99
122
  Vanity supports multiple SQL stores (like MySQL, MariaDB, Postgres, Sqlite,
100
123
  etc.) using ActiveRecord, which is built into Rails. If you're using
@@ -133,7 +156,7 @@ $ rails generate vanity
133
156
  $ rake db:migrate
134
157
  ```
135
158
 
136
- ##### Forking servers and reconnecting
159
+ #### Forking servers and reconnecting
137
160
 
138
161
  If you're using a forking server (like Passenger or Unicorn), you should
139
162
  reconnect after a new worker is created:
@@ -165,7 +188,25 @@ Vanity.connect!(
165
188
  )
166
189
  ```
167
190
 
168
- #### Step 1.3
191
+ ### Initialization
192
+
193
+ If you're using Rails, this is done automagically. Otherwise, some manual setup is required, for example on an app's booting:
194
+
195
+ ```
196
+ $redis = Redis.new # or from elsewhere
197
+ Vanity.configure do |config|
198
+ # ... any config
199
+ end
200
+ Vanity.connect!(
201
+ adapter: :redis,
202
+ redis: $redis
203
+ )
204
+ Vanity.load!
205
+ ```
206
+
207
+ ### User identification
208
+
209
+ #### Rails
169
210
 
170
211
  Turn Vanity on, and pass a reference to a method that identifies a user. For
171
212
  example:
@@ -179,7 +220,28 @@ end
179
220
  For more information, please see the [identity
180
221
  documentation](http://vanity.labnotes.org/identity.html).
181
222
 
182
- ### **Step 2:** Define your first A/B test
223
+ #### Other
224
+
225
+ Vanity pulls the identity from a "context" object that responds to `vanity_identity`, so we need to define a `Vanity.context` (this is how the [ActionMailer integration](https://github.com/assaf/vanity/blob/master/lib/vanity/frameworks/rails.rb#L107-L133) works):
226
+
227
+ ```
228
+ class AVanityContext
229
+ def vanity_identity
230
+ "123"
231
+ end
232
+ end
233
+
234
+ Vanity.context = AVanityContext.new() # Any object that responds to `#vanity_identity`
235
+ ```
236
+
237
+ If you're using plain ruby objects, you could also alias something in your identity model to respond similarly and then set that as the vanity context:
238
+ ```
239
+ class User
240
+ alias_method :vanity_identity, :id
241
+ end
242
+ ```
243
+
244
+ ### Define a A/B test
183
245
 
184
246
  This experiment goes in the file `experiments/price_options.rb`:
185
247
 
@@ -200,15 +262,25 @@ metric "Signup (Activation)" do
200
262
  end
201
263
  ```
202
264
 
203
- ### **Step 3:** Present the different options to your users
265
+ ### Present the different options to your users
266
+
267
+ In Rails' templates, this is straightforward:
204
268
 
205
269
  ```erb
206
270
  <h2>Get started for only $<%= ab_test :price_options %> a month!</h2>
207
271
  ```
208
272
 
209
- ### **Step 4:** Measure conversion
273
+ Outside of templates:
210
274
 
211
- Conversions are created via the `Vanity.track!` method. For example:
275
+ ```
276
+ Vanity.ab_test(:invite_subject)
277
+ ```
278
+
279
+ ### Measure conversion
280
+
281
+ Conversions are created via the `Vanity.track!` method. A user should already be added to an experiment, via `ab_test` before this is called - otherwise, the conversion will be tracked, but the user will not be added to the experiment.
282
+
283
+ For example, in Rails:
212
284
 
213
285
  ```ruby
214
286
  class SignupController < ApplicationController
@@ -224,12 +296,26 @@ class SignupController < ApplicationController
224
296
  end
225
297
  ```
226
298
 
227
- ### **Step 5:** Check the report:
299
+ Outside of an Rails controller, for example in a Rack handler:
300
+
301
+ ```
302
+ identity_object = Identity.new(env['rack.session'])
303
+ Vanity.track!(:click, {
304
+ # can be any object that responds to `to_s` with a string
305
+ # that contains the unique identifier or the string identifier itself
306
+ :identity=>identity_object,
307
+ :values=>[1] # optional
308
+ })
309
+ ```
310
+
311
+ ### Check the report
228
312
 
229
313
  ```sh
230
314
  vanity report --output vanity.html
231
315
  ```
232
316
 
317
+ #### Rails report dashboard
318
+
233
319
  To view metrics and experiment results with the dashboard in Rails 3 & Rails
234
320
  4:
235
321
 
@@ -7,7 +7,7 @@ GIT
7
7
  PATH
8
8
  remote: ..
9
9
  specs:
10
- vanity (2.2.8)
10
+ vanity (2.2.9)
11
11
  i18n
12
12
 
13
13
  GEM
@@ -7,7 +7,7 @@ GIT
7
7
  PATH
8
8
  remote: .././
9
9
  specs:
10
- vanity (2.2.8)
10
+ vanity (2.2.9)
11
11
  i18n
12
12
 
13
13
  GEM
@@ -7,7 +7,7 @@ GIT
7
7
  PATH
8
8
  remote: ..
9
9
  specs:
10
- vanity (2.2.8)
10
+ vanity (2.2.9)
11
11
  i18n
12
12
 
13
13
  GEM
@@ -7,7 +7,7 @@ GIT
7
7
  PATH
8
8
  remote: ../
9
9
  specs:
10
- vanity (2.2.8)
10
+ vanity (2.2.9)
11
11
  i18n
12
12
 
13
13
  GEM
@@ -7,7 +7,7 @@ GIT
7
7
  PATH
8
8
  remote: ../
9
9
  specs:
10
- vanity (2.2.8)
10
+ vanity (2.2.9)
11
11
  i18n
12
12
 
13
13
  GEM
@@ -0,0 +1,28 @@
1
+ require "vanity/adapters/active_record_adapter"
2
+
3
+ class AddVanityUniqueIndexes < ActiveRecord::Migration
4
+ # Helper methods to ensure we're connecting to the right database, see
5
+ # https://github.com/assaf/vanity/issues/295.
6
+
7
+ def connection
8
+ @connection ||= ActiveRecord::Base.connection
9
+ end
10
+ alias_method :default_connection, :connection
11
+
12
+ def with_vanity_connection
13
+ @connection = Vanity::Adapters::ActiveRecordAdapter::VanityRecord.connection
14
+ yield
15
+ @connection = default_connection
16
+ end
17
+
18
+ def change
19
+ with_vanity_connection do
20
+ remove_index :vanity_experiments, [:experiment_id]
21
+ add_index :vanity_experiments, [:experiment_id], :unique => true
22
+
23
+ remove_index :vanity_conversions, :name => "by_experiment_id_and_alternative", :unique => true
24
+ add_index :vanity_conversions, [:vanity_experiment_id, :alternative], :name => "by_experiment_id_and_alternative", :unique => true
25
+ end
26
+ end
27
+
28
+ end
@@ -1,3 +1,5 @@
1
+ require "vanity/adapters/active_record_adapter"
2
+
1
3
  class VanityMigration < ActiveRecord::Migration
2
4
  # Helper methods to ensure we're connecting to the right database, see
3
5
  # https://github.com/assaf/vanity/issues/295.
@@ -36,14 +38,14 @@ class VanityMigration < ActiveRecord::Migration
36
38
  t.datetime :created_at
37
39
  t.datetime :completed_at
38
40
  end
39
- add_index :vanity_experiments, [:experiment_id]
41
+ add_index :vanity_experiments, [:experiment_id], :unique => true
40
42
 
41
43
  create_table :vanity_conversions do |t|
42
44
  t.integer :vanity_experiment_id
43
45
  t.integer :alternative
44
46
  t.integer :conversions
45
47
  end
46
- add_index :vanity_conversions, [:vanity_experiment_id, :alternative], :name => "by_experiment_id_and_alternative"
48
+ add_index :vanity_conversions, [:vanity_experiment_id, :alternative], :name => "by_experiment_id_and_alternative", :unique => true
47
49
 
48
50
  create_table :vanity_participants do |t|
49
51
  t.string :experiment_id
@@ -0,0 +1,15 @@
1
+ require 'rails/generators'
2
+ require 'rails/generators/migration'
3
+
4
+ class Vanity::AddUniqueIndexesGenerator < Rails::Generators::Base
5
+ include Rails::Generators::Migration
6
+ source_root File.expand_path('../../templates', __FILE__)
7
+
8
+ def self.next_migration_number(path)
9
+ Time.now.utc.strftime("%Y%m%d%H%M%S")
10
+ end
11
+
12
+ def create_model_file
13
+ migration_template "add_unique_indexes_migration.rb", "db/migrate/add_vanity_unique_indexes.rb"
14
+ end
15
+ end
@@ -19,10 +19,20 @@ module Vanity
19
19
  end
20
20
 
21
21
  def self.rails_agnostic_find_or_create_by(method, value)
22
- if respond_to? :find_or_create_by
23
- find_or_create_by(method => value)
24
- else
25
- send :"find_or_create_by_#{method}", value
22
+ retried = false
23
+ begin
24
+ if respond_to? :find_or_create_by
25
+ find_or_create_by(method => value)
26
+ else
27
+ send :"find_or_create_by_#{method}", value
28
+ end
29
+ rescue ActiveRecord::RecordNotUnique
30
+ if retried
31
+ raise
32
+ else
33
+ retried = true
34
+ retry
35
+ end
26
36
  end
27
37
  end
28
38
  end
@@ -1,5 +1,5 @@
1
1
  module Vanity
2
- VERSION = "2.2.8"
2
+ VERSION = "2.2.9"
3
3
 
4
4
  module Version
5
5
  version = VERSION.to_s.split(".").map { |i| i.to_i }
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: vanity
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.2.8
4
+ version: 2.2.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Assaf Arkin
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-09-26 00:00:00.000000000 Z
11
+ date: 2018-02-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: i18n
@@ -177,7 +177,9 @@ files:
177
177
  - gemfiles/rails42_protected_attributes.gemfile.lock
178
178
  - gemfiles/rails5.gemfile
179
179
  - gemfiles/rails5.gemfile.lock
180
+ - lib/generators/templates/add_unique_indexes_migration.rb
180
181
  - lib/generators/templates/vanity_migration.rb
182
+ - lib/generators/vanity/add_unique_indexes_generator.rb
181
183
  - lib/generators/vanity/views_generator.rb
182
184
  - lib/generators/vanity_generator.rb
183
185
  - lib/vanity.rb
@@ -290,7 +292,7 @@ metadata: {}
290
292
  post_install_message: To get started run vanity --help
291
293
  rdoc_options:
292
294
  - "--title"
293
- - Vanity 2.2.8
295
+ - Vanity 2.2.9
294
296
  - "--main"
295
297
  - README.md
296
298
  - "--webcvs"