vanity 2.2.8 → 2.2.9

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.
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"