vanity 1.9.0 → 1.9.1

Sign up to get free protection for your applications and to get access to all the features.
data/.travis.yml CHANGED
@@ -11,8 +11,7 @@ rvm:
11
11
  env:
12
12
  - DB=mongodb
13
13
  - DB=redis
14
- - DB=mysql
15
- #- DB=postgres
14
+ - DB=active_record
16
15
  gemfile:
17
16
  - Gemfile
18
17
  - gemfiles/rails3.gemfile
@@ -22,8 +21,6 @@ gemfile:
22
21
  before_script:
23
22
  - 'echo ''gem: --no-ri --no-rdoc'' > ~/.gemrc' # skip installing docs for gems
24
23
  - if [[ "`basename $BUNDLE_GEMFILE`" == "Gemfile" ]]; then rvm rubygems 1.8.25; fi # Rubygems 2.0.x fails with Rails 2.3
25
- - if [[ "$DB" == "mysql" ]]; then mysql -e 'create database vanity_test;' >/dev/null; fi
26
- #- if [[ "$DB" == "pgsql" ]]; then psql -c 'create database vanity_test;' -U postgres >/dev/null; fi
27
24
  matrix:
28
25
  exclude:
29
26
  # Rails 2 is not officially supported on Ruby 1.9.3
@@ -34,7 +31,7 @@ matrix:
34
31
  env: DB=redis
35
32
  gemfile: Gemfile
36
33
  - rvm: 1.9.3
37
- env: DB=mysql
34
+ env: DB=active_record
38
35
  gemfile: Gemfile
39
36
  # Rails <= 3.2 is not officially supported on Ruby 2.0.0
40
37
  - rvm: 2.0.0
@@ -44,7 +41,7 @@ matrix:
44
41
  env: DB=redis
45
42
  gemfile: Gemfile
46
43
  - rvm: 2.0.0
47
- env: DB=mysql
44
+ env: DB=active_record
48
45
  gemfile: Gemfile
49
46
  - rvm: 2.0.0
50
47
  env: DB=mongodb
@@ -53,7 +50,7 @@ matrix:
53
50
  env: DB=redis
54
51
  gemfile: gemfiles/rails3.gemfile
55
52
  - rvm: 2.0.0
56
- env: DB=mysql
53
+ env: DB=active_record
57
54
  gemfile: gemfiles/rails3.gemfile
58
55
  # Rails >=4 officially supports >= Ruby 1.9.3
59
56
  - rvm: 1.8.7
@@ -63,7 +60,7 @@ matrix:
63
60
  env: DB=redis
64
61
  gemfile: gemfiles/rails4.gemfile
65
62
  - rvm: 1.8.7
66
- env: DB=mysql
63
+ env: DB=active_record
67
64
  gemfile: gemfiles/rails4.gemfile
68
65
  allow_failures:
69
66
  - rvm: ruby-head
@@ -74,5 +71,5 @@ matrix:
74
71
  env: DB=redis
75
72
  gemfile: gemfiles/rails31.gemfile
76
73
  - rvm: 2.0.0
77
- env: DB=mysql
74
+ env: DB=active_record
78
75
  gemfile: gemfiles/rails31.gemfile
data/CHANGELOG CHANGED
@@ -1,3 +1,13 @@
1
+ == 1.9.0 (2014-04-20)
2
+
3
+ Include db:reset in blacklist for autoconnect. (@phillbaker)
4
+ Add grace period to Vanity::Metric updated_at (@stangel)
5
+ Add timestamps to vanity_participants (@inkredabull)
6
+ Use sqlite3 for all active record adapter tests & updates to test tasks (@phillbaker)
7
+ Fix ActiveRecord participant retrieval with non-primitive identities. (@phillbaker)
8
+ Replace instance variables with locals to fix CLI. (@phillbaker)
9
+ Replace js encoding with server-side call. (@phillbaker)
10
+
1
11
  == 1.9.0 (2014-01-18)
2
12
 
3
13
  Change order of preference for vanity identity, from highest to lowest: URL param, cookie, method call, assignment. (@phillbaker)
data/Gemfile CHANGED
@@ -13,9 +13,7 @@ gem "redis", ">= 2.1"
13
13
  gem "redis-namespace", ">= 1.1.0"
14
14
  gem "bson_ext"
15
15
  gem "mongo"
16
- gem "mysql"
17
16
  gem "sqlite3"
18
- # gem "pg"
19
17
 
20
18
  # Math libraries
21
19
  gem "backports", :platforms => :mri_18
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- vanity (1.9.0)
4
+ vanity (1.9.1)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
@@ -55,7 +55,6 @@ GEM
55
55
  metaclass (~> 0.0.1)
56
56
  mongo (1.6.0)
57
57
  bson (= 1.6.0)
58
- mysql (2.8.1)
59
58
  passenger (2.2.15)
60
59
  fastthread (>= 1.0.1)
61
60
  rack
@@ -97,12 +96,12 @@ DEPENDENCIES
97
96
  appraisal (>= 1.0.0.beta2)
98
97
  backports
99
98
  bson_ext
99
+ bundler (>= 1.0.0)
100
100
  garb
101
101
  integration
102
102
  jekyll
103
103
  mocha
104
104
  mongo
105
- mysql
106
105
  passenger (~> 2.0)
107
106
  rack
108
107
  rails (~> 2.3.8)
data/README.rdoc CHANGED
@@ -33,7 +33,7 @@ Add to your Gemfile:
33
33
 
34
34
  ==== Step 1.2
35
35
 
36
- Choose a datastore that best fits your needs and preferences for storing experiment results. Choose one of: Redis, MongoDB or an SQL database. While Redis is usually faster, it may add additional complexity to your stack.
36
+ Choose a datastore that best fits your needs and preferences for storing experiment results. Choose one of: Redis, MongoDB or an SQL database. While Redis is usually faster, it may add additional complexity to your stack. Datastores should be configured using a <code>config/vanity.yml</code>.
37
37
 
38
38
  ===== Redis Setup
39
39
 
@@ -42,6 +42,8 @@ Add to your Gemfile:
42
42
  gem "redis", ">= 2.1"
43
43
  gem "redis-namespace", ">= 1.1.0"
44
44
 
45
+ By default Vanity is configured to use Redis on localhost port 6379 with database 0.
46
+
45
47
  ===== MongoDB Setup
46
48
 
47
49
  Add to your Gemfile:
@@ -49,12 +51,39 @@ Add to your Gemfile:
49
51
  gem "bson_ext"
50
52
  gem "mongo"
51
53
 
54
+ A sample <code>config/vanity.yml</code> might look like:
55
+
56
+ development:
57
+ adapter: mongodb
58
+ database: analytics
59
+ test:
60
+ collecting: false
61
+ production:
62
+ adapter: mongodb
63
+ database: analytics
64
+
65
+
52
66
  ===== SQL Database Setup
53
67
 
54
68
  Vanity supports multiple SQL stores (like MySQL, MariaDB, Postgres, Sqlite, etc.) using ActiveRecord, which is built into Rails. If you're using DataMapper, Sequel or another persistence framework, add to your Gemfile:
55
69
 
56
70
  gem "active_record"
57
71
 
72
+ A sample <code>config/vanity.yml</code> might look like:
73
+
74
+ development:
75
+ adapter: active_record
76
+ active_record_adapter: sqlite3
77
+ database: db/development.sqlite3
78
+ test:
79
+ collecting: false
80
+ production:
81
+ adapter: active_record
82
+ active_record_adapter: mysql
83
+ database: vanity_test
84
+ pool: 5
85
+ timeout: 5000
86
+
58
87
  If you're going to store data in the database, run the generator and migrations to create the database schema:
59
88
 
60
89
  $ rails generate vanity
@@ -113,7 +142,7 @@ Conversions are created via the <code>track!</code> method. For example:
113
142
 
114
143
  To view metrics and experiment results with the dashboard in Rails 3 & Rails 4:
115
144
 
116
- rails generate controller Vanity
145
+ rails generate controller --helper=false Vanity
117
146
 
118
147
  In <code>config/routes.rb</code>, add:
119
148
 
@@ -127,15 +156,13 @@ The controller should look like:
127
156
 
128
157
  == Registering participants with Javascript
129
158
 
130
- If robots or spiders make up a significant portion of your sites traffic they can affect your conversion rate. Vanity can optionally add participants to the experiments using asynchronous javascript callbacks, which will keep almost all robots out. To set this up simply do the following:
159
+ If robots or spiders make up a significant portion of your sites traffic they can affect your conversion rate. Vanity can optionally add participants to the experiments using asynchronous javascript callbacks, which will keep many robots out. For those robots that do execute Javascript and are well-behaved (like Googlebot), Vanity filters out requests based on their user-agent string.
160
+
161
+ To set this up simply do the following:
131
162
 
132
163
  * Add <code>Vanity.playground.use_js!</code>
133
164
  * Set <code>Vanity.playground.add_participant_path = '/path/to/vanity/action'</code>, this should point to the add_participant path that is added with Vanity::Rails::Dashboard, make sure that this action is accessible by all users (ie does not require authentication).
134
- * Add <code><%= vanity_js %></code> to any page that needs uses an ab_test. <code>vanity_js</code> needs to be included after your call to ab_test so that it knows which version of the experiment the participant is a member of. The helper will render nothing if the there are no ab_tests running on the current page, so adding <code>vanity_js</code> to the bottom of your layouts is a good option. Keep in mind that if you call <code>use_js!</code> and don't include <code>vanity_js</code> in your view no participants will be recorded.
135
-
136
- === <b>Update regarding using AJAX registration to avoid robots</b>
137
-
138
- As of January 2014, as reported in {issue 175}[https://github.com/assaf/vanity/issues/175], Googlebot is now executing AJAX requests to the original domain of the page, which includes the current implementation of the JS callback. This means that Googlebot-originating requests may be included in experiments, greatly skewing results. A fix is being worked on, please follow the issue for further updates.
165
+ * Add <code><%= vanity_js %></code> to any page that XX an ab_test. <code>vanity_js</code> needs to be included after your call to ab_test so that it knows which version of the experiment the participant is a member of. The helper will render nothing if the there are no ab_tests running on the current page, so adding <code>vanity_js</code> to the bottom of your layouts is a good option. Keep in mind that if you call <code>use_js!</code> and don't include <code>vanity_js</code> in your view no participants will be recorded.
139
166
 
140
167
  == Compatibility
141
168
 
@@ -160,11 +187,10 @@ For view tests/specs or integration testing, it's handy to set the outcome of an
160
187
  == Contributing
161
188
 
162
189
  * Fork the project
163
- * Please use a topic branch to make your changes, it's easier to test them that way
164
- * To set up the test suite run bundler, then run `rake appraisal:install` to prepare the test suite to run against multiple versions of Rails
190
+ * Please use a feature branch to make your changes, it's easier to test them that way
191
+ * To set up the test suite run `bundle`, then run `appraisal install` to prepare the test suite to run against multiple versions of Rails
165
192
  * Fix, patch, enhance, document, improve, sprinkle pixie dust
166
- * At minimum run `rake appraisal test`, if possible, please run rake test:all
167
- * Tests. Please. Run rake test, of if you can, rake test:all
193
+ * Tests. Please. Run `appraisal rake test`, of if you can, `rake test:all`. (This project uses Travis CI where the test suite is run against multiple versions of ruby, rails and backends.)
168
194
  * Send a pull request on GitHub
169
195
 
170
196
 
data/Rakefile CHANGED
@@ -1,29 +1,5 @@
1
1
  require "rake/testtask"
2
-
3
- # -- Building stuff --
4
-
5
- spec = Gem::Specification.load(Dir["*.gemspec"].first)
6
-
7
- desc "Build the Gem"
8
- task :build do
9
- sh "gem build #{spec.name}.gemspec"
10
- end
11
-
12
- desc "Install #{spec.name} locally"
13
- task :install=>:build do
14
- sudo = "sudo" unless File.writable?( Gem::ConfigMap[:bindir])
15
- sh "#{sudo} gem install #{spec.name}-#{spec.version}.gem"
16
- end
17
-
18
- desc "Push new release to gemcutter and git tag"
19
- task :push=>["build"] do
20
- sh "git push"
21
- puts "Tagging version #{spec.version} .."
22
- sh "git tag v#{spec.version}"
23
- sh "git push --tag"
24
- puts "Building and pushing gem .."
25
- sh "gem push #{spec.name}-#{spec.version}.gem"
26
- end
2
+ require "bundler/gem_tasks"
27
3
 
28
4
 
29
5
  # -- Testing stuff --
@@ -70,7 +46,7 @@ task "test:setup" do
70
46
  end
71
47
 
72
48
  # These are all the adapters we're going to test with.
73
- ADAPTERS = %w{redis mongodb mysql}
49
+ ADAPTERS = %w{redis mongodb active_record}
74
50
 
75
51
  desc "Test using different back-ends"
76
52
  task "test:adapters", :adapter do |t, args|
@@ -80,7 +56,7 @@ task "test:adapters", :adapter do |t, args|
80
56
  adapters = args.adapter ? [args.adapter] : ADAPTERS
81
57
  adapters.each do |adapter|
82
58
  puts "** Testing #{adapter} adapter"
83
- sh "rake appraisal test DB=#{adapter} #{'--trace' if Rake.application.options.trace}"
59
+ sh "bundle exec appraisal rake test DB=#{adapter} #{'--trace' if Rake.application.options.trace}"
84
60
  end
85
61
  rescue LoadError
86
62
  warn "The appraisal gem must be available"
@@ -107,40 +83,20 @@ end
107
83
  task(:clobber) { rm_rf "tmp" }
108
84
 
109
85
 
110
- # -- Documenting stuff --
111
-
112
- begin
113
- require "yard"
114
- YARD::Rake::YardocTask.new(:yardoc) do |task|
115
- task.files = FileList["lib/**/*.rb"].exclude("lib/vanity/backport.rb")
116
- task.options = "--output", "html/api", "--title", "Vanity #{spec.version}", "--main", "README.rdoc", "--files", "CHANGELOG"
117
- end
118
- rescue LoadError
119
- end
86
+ # -- Documenting stuff -- #TODO make sure works under 1.9/2.0
120
87
 
121
88
  desc "Jekyll generates the main documentation (sans API)"
122
89
  task(:jekyll) { sh "jekyll", "doc", "html" }
123
- file "html/vanity.pdf"=>:jekyll do |t|
124
- pages = %w{index metrics ab_testing rails email identity configuring contributing}.map{ |p| "html/#{p}.html" }
125
- args = %w{--disable-javascript --outline --title Vanity --header-html doc/_layouts/_header.html --print-media-type}
126
- args.concat %w{--margin-left 20 --margin-right 20 --margin-top 20 --margin-bottom 20 --header-spacing 5}
127
- args.concat pages << t.name
128
- sh "wkhtmltopdf", *args
129
- end
130
90
 
131
- file "html/vanity-api.zip"=>:yardoc do |t|
132
- Dir.chdir "html" do
133
- sh "zip vanity-api.zip -r api"
134
- end
135
- end
136
- desc "Create documentation in docs directory (including API)"
137
- task :docs=>[:jekyll, :yardoc, "html/vanity-api.zip", "html/vanity.pdf"]
91
+ desc "Create documentation in docs directory"
92
+ task :docs=>[:jekyll]
93
+
138
94
  desc "Remove temporary files and directories"
139
- task(:clobber) { rm_rf "html" ; rm_rf ".yardoc" }
95
+ task(:clobber) { rm_rf "html" }
140
96
 
141
- desc "Publish documentation to vanity.labnotes.org"
97
+ desc "Publish documentation to vanity.labnotes.org via Github Pages on gh-pages git branch"
142
98
  task :publish=>[:clobber, :docs] do
143
- sh "rsync -cr --del --progress html/ labnotes.org:/var/www/vanity/"
99
+ # TODO
144
100
  end
145
101
 
146
102
 
data/doc/rails.textile CHANGED
@@ -7,7 +7,6 @@ title: Using with Rails
7
7
  # "Installing Vanity":#install
8
8
  # "Configuring Vanity":#config
9
9
  # "Test Environment":#test
10
- # "Dashboard":#dashboard
11
10
  # "Unicorn and Forking Servers":#fork
12
11
  </div>
13
12
 
@@ -78,27 +77,6 @@ h4. Enabling/disable collection
78
77
  When collection is off, Vanity doesn't connect to the database server, so there's no need to set a database configuration for these environments.
79
78
 
80
79
 
81
- h3(#dashboard). Dashboard
82
-
83
- Start by adding a new resource in @config/routes.rb@:
84
-
85
- <pre>
86
- map.vanity "/vanity/:action/:id", :controller=>:vanity
87
- </pre>
88
-
89
- Create a new controller for Vanity:
90
-
91
- <pre>
92
- class VanityController < ApplicationController
93
- include Vanity::Rails::Dashboard
94
- end
95
- </pre>
96
-
97
- Now open your browser to "http://localhost:3000/vanity":http://localhost:3000/vanity.
98
-
99
- The Dashboard renders complete HTML pages with CSS and all necessary JavaScript libraries. Thankfully, HTML is forgiving enough that it will render correctly even with your existing application layout. You can decide to keep your layout, or tell the controller to set @layout false@.
100
-
101
-
102
80
  h3(#fork). Unicorn and Forking Servers
103
81
 
104
82
  Unicorn forks the master process to create worker processes efficiently. Since the master processes opens a connection to the database, all workers end up sharing that connection, resulting in ugly contention issues.
@@ -7,7 +7,6 @@ gem "redis", ">= 2.1"
7
7
  gem "redis-namespace", ">= 1.1.0"
8
8
  gem "bson_ext"
9
9
  gem "mongo"
10
- gem "mysql"
11
10
  gem "sqlite3"
12
11
  gem "backports", :platforms=>:mri_18
13
12
  gem "integration"
@@ -7,7 +7,7 @@ GIT
7
7
  PATH
8
8
  remote: ..
9
9
  specs:
10
- vanity (1.9.0)
10
+ vanity (1.9.1)
11
11
 
12
12
  GEM
13
13
  remote: https://rubygems.org/
@@ -90,7 +90,6 @@ GEM
90
90
  metaclass (~> 0.0.1)
91
91
  mongo (1.6.0)
92
92
  bson (= 1.6.0)
93
- mysql (2.8.1)
94
93
  passenger (3.0.11)
95
94
  daemon_controller (>= 0.2.5)
96
95
  fastthread (>= 1.0.1)
@@ -151,13 +150,13 @@ DEPENDENCIES
151
150
  appraisal (>= 1.0.0.beta2)
152
151
  backports
153
152
  bson_ext
153
+ bundler (>= 1.0.0)
154
154
  fastthread!
155
155
  garb
156
156
  integration
157
157
  jekyll
158
158
  mocha
159
159
  mongo
160
- mysql
161
160
  passenger (~> 3.0)
162
161
  rack
163
162
  rails (= 3.0.11)
@@ -7,7 +7,6 @@ gem "redis", ">= 2.1"
7
7
  gem "redis-namespace", ">= 1.1.0"
8
8
  gem "bson_ext"
9
9
  gem "mongo"
10
- gem "mysql"
11
10
  gem "sqlite3"
12
11
  gem "backports", :platforms=>:mri_18
13
12
  gem "integration"
@@ -7,7 +7,7 @@ GIT
7
7
  PATH
8
8
  remote: ..
9
9
  specs:
10
- vanity (1.9.0)
10
+ vanity (1.9.1)
11
11
 
12
12
  GEM
13
13
  remote: https://rubygems.org/
@@ -90,7 +90,6 @@ GEM
90
90
  mongo (1.6.0)
91
91
  bson (= 1.6.0)
92
92
  multi_json (1.1.0)
93
- mysql (2.8.1)
94
93
  passenger (3.0.11)
95
94
  daemon_controller (>= 0.2.5)
96
95
  fastthread (>= 1.0.1)
@@ -160,13 +159,13 @@ DEPENDENCIES
160
159
  appraisal (>= 1.0.0.beta2)
161
160
  backports
162
161
  bson_ext
162
+ bundler (>= 1.0.0)
163
163
  fastthread!
164
164
  garb
165
165
  integration
166
166
  jekyll
167
167
  mocha
168
168
  mongo
169
- mysql
170
169
  passenger (~> 3.0)
171
170
  rack
172
171
  rails (= 3.1.3)
@@ -7,7 +7,6 @@ gem "redis", ">= 2.1"
7
7
  gem "redis-namespace", ">= 1.1.0"
8
8
  gem "bson_ext"
9
9
  gem "mongo"
10
- gem "mysql"
11
10
  gem "sqlite3"
12
11
  gem "backports", :platforms=>:mri_18
13
12
  gem "integration"
@@ -7,7 +7,7 @@ GIT
7
7
  PATH
8
8
  remote: ..
9
9
  specs:
10
- vanity (1.9.0)
10
+ vanity (1.9.1)
11
11
 
12
12
  GEM
13
13
  remote: https://rubygems.org/
@@ -91,7 +91,6 @@ GEM
91
91
  mongo (1.6.0)
92
92
  bson (= 1.6.0)
93
93
  multi_json (1.1.0)
94
- mysql (2.8.1)
95
94
  passenger (3.0.11)
96
95
  daemon_controller (>= 0.2.5)
97
96
  fastthread (>= 1.0.1)
@@ -160,13 +159,13 @@ DEPENDENCIES
160
159
  appraisal (>= 1.0.0.beta2)
161
160
  backports
162
161
  bson_ext
162
+ bundler (>= 1.0.0)
163
163
  fastthread!
164
164
  garb
165
165
  integration
166
166
  jekyll
167
167
  mocha
168
168
  mongo
169
- mysql
170
169
  passenger (~> 3.0)
171
170
  rack
172
171
  rails (= 3.2.1)
@@ -7,7 +7,6 @@ gem "redis", ">= 2.1"
7
7
  gem "redis-namespace", ">= 1.1.0"
8
8
  gem "bson_ext"
9
9
  gem "mongo"
10
- gem "mysql"
11
10
  gem "sqlite3"
12
11
  gem "backports", :platforms=>:mri_18
13
12
  gem "integration"
@@ -7,7 +7,7 @@ GIT
7
7
  PATH
8
8
  remote: ..
9
9
  specs:
10
- vanity (1.9.0)
10
+ vanity (1.9.1)
11
11
 
12
12
  GEM
13
13
  remote: https://rubygems.org/
@@ -88,7 +88,6 @@ GEM
88
88
  mongo (1.9.2)
89
89
  bson (~> 1.9.2)
90
90
  multi_json (1.8.2)
91
- mysql (2.9.1)
92
91
  passenger (3.0.21)
93
92
  daemon_controller (>= 1.0.0)
94
93
  fastthread (>= 1.0.1)
@@ -158,13 +157,13 @@ DEPENDENCIES
158
157
  appraisal (>= 1.0.0.beta2)
159
158
  backports
160
159
  bson_ext
160
+ bundler (>= 1.0.0)
161
161
  fastthread!
162
162
  garb
163
163
  integration
164
164
  jekyll
165
165
  mocha
166
166
  mongo
167
- mysql
168
167
  passenger (~> 3.0)
169
168
  rack
170
169
  rails (= 4.0.0)
@@ -35,6 +35,7 @@ class VanityMigration < ActiveRecord::Migration
35
35
  t.integer :shown
36
36
  t.integer :seen
37
37
  t.integer :converted
38
+ t.timestamps
38
39
  end
39
40
  add_index :vanity_participants, [:experiment_id]
40
41
  add_index :vanity_participants, [:experiment_id, :identity], :name => "by_experiment_id_and_identity"
@@ -35,6 +35,7 @@ class VanityMigration < ActiveRecord::Migration
35
35
  t.integer :shown
36
36
  t.integer :seen
37
37
  t.integer :converted
38
+ t.timestamps
38
39
  end
39
40
  add_index :vanity_participants, [:experiment_id]
40
41
  add_index :vanity_participants, [:experiment_id, :identity], :name => "by_experiment_id_and_identity"
@@ -4,7 +4,7 @@ module Vanity
4
4
  class << self
5
5
  # Creates new connection to underlying datastore and returns suitable
6
6
  # adapter (adapter object extends AbstractAdapter and wraps the
7
- # connection). Vanity.playgroup.establish_connection uses this.
7
+ # connection). Vanity.playground.establish_connection uses this.
8
8
  #
9
9
  # @since 1.4.0
10
10
  def establish_connection(spec)
@@ -34,12 +34,24 @@ module Vanity
34
34
 
35
35
  # Metric model
36
36
  class VanityMetric < VanityRecord
37
+ UPDATED_AT_GRACE_PERIOD = 1.minute
37
38
  self.table_name = :vanity_metrics
38
39
  has_many :vanity_metric_values
39
40
 
40
41
  def self.retrieve(metric)
41
42
  rails_agnostic_find_or_create_by(:metric_id, metric.to_s)
42
43
  end
44
+
45
+ def touch_with_grace_period
46
+ now = Time.now
47
+ self.updated_at = now if updated_before_grace_period?(now)
48
+ end
49
+
50
+ private
51
+
52
+ def updated_before_grace_period?(now)
53
+ now - updated_at >= UPDATED_AT_GRACE_PERIOD
54
+ end
43
55
  end
44
56
 
45
57
  # Metric value
@@ -78,24 +90,21 @@ module Vanity
78
90
  self.table_name = :vanity_participants
79
91
  attr_accessible :experiment_id, :identity, :seen, :shown, :converted if needs_attr_accessible?
80
92
 
81
- # Finds the participant by experiment and identity. If
82
- # create is true then it will create the participant
83
- # if not found. If a hash is passed then this will be
84
- # passed to create if creating, or will be used to
85
- # update the found participant.
93
+ # Finds the participant by experiment and identity. If create is true
94
+ # then it will create the participant if not found. If a hash is
95
+ # passed then this will be passed to create if creating, or will be
96
+ # used to update the found participant.
86
97
  def self.retrieve(experiment, identity, create = true, update_with = nil)
87
98
  if record = VanityParticipant.first(:conditions=>{ :experiment_id=>experiment.to_s, :identity=>identity.to_s })
88
99
  record.update_attributes(update_with) if update_with
89
100
  elsif create
90
- record = VanityParticipant.create({ :experiment_id=>experiment.to_s, :identity=>identity }.merge(update_with || {}))
101
+ record = VanityParticipant.create({ :experiment_id=>experiment.to_s, :identity=>identity.to_s }.merge(update_with || {}))
91
102
  end
92
103
  record
93
104
  end
94
105
  end
95
106
 
96
107
  def initialize(options)
97
-
98
-
99
108
  @options = options.inject({}) { |h,kv| h[kv.first.to_s] = kv.last ; h }
100
109
  if @options["active_record_adapter"] && (@options["active_record_adapter"] != "default")
101
110
  @options["adapter"] = @options["active_record_adapter"]
@@ -136,7 +145,7 @@ module Vanity
136
145
  record.vanity_metric_values.create(:date => timestamp.to_date.to_s, :index => index, :value => value)
137
146
  end
138
147
 
139
- record.updated_at = Time.now
148
+ record.touch_with_grace_period
140
149
  record.save
141
150
  end
142
151
 
@@ -150,10 +159,10 @@ module Vanity
150
159
  group_by = "#{connection.quote_column_name('date')}"
151
160
 
152
161
  values = record.vanity_metric_values.all(
153
- :select => select,
154
- :conditions => conditions,
155
- :order => order,
156
- :group => group_by
162
+ :select => select,
163
+ :conditions => conditions,
164
+ :order => order,
165
+ :group => group_by
157
166
  )
158
167
 
159
168
  dates.map do |date|
@@ -211,9 +220,9 @@ module Vanity
211
220
  conversions = record.vanity_conversions.sum(:conversions, :conditions => {:alternative => alternative})
212
221
 
213
222
  {
214
- :participants => participants,
215
- :converted => converted,
216
- :conversions => conversions
223
+ :participants => participants,
224
+ :converted => converted,
225
+ :conversions => conversions
217
226
  }
218
227
  end
219
228
 
@@ -155,9 +155,9 @@ module Vanity
155
155
  end
156
156
 
157
157
  def ab_add_participant(experiment, alternative, identity)
158
- call_redis_with_failover(experiment, alternative, identity) do
159
- @experiments.sadd "#{experiment}:alts:#{alternative}:participants", identity
160
- end
158
+ call_redis_with_failover(experiment, alternative, identity) do
159
+ @experiments.sadd "#{experiment}:alts:#{alternative}:participants", identity
160
+ end
161
161
  end
162
162
 
163
163
  def ab_seen(experiment, identity, alternative)
@@ -177,7 +177,7 @@ module Vanity
177
177
  if @experiments.sismember "#{experiment}:alts:#{alternative.id}:participants", identity
178
178
  return alternative.id
179
179
  end
180
- end
180
+ end
181
181
  nil
182
182
  end
183
183
  end
@@ -16,6 +16,7 @@ module Vanity
16
16
  'db:migrate',
17
17
  'db:migrate:status',
18
18
  'db:rollback',
19
+ 'db:reset',
19
20
  'db:schema:cache:clear',
20
21
  'db:schema:cache:dump',
21
22
  'db:schema:dump',
@@ -15,7 +15,7 @@ module Vanity
15
15
  path_or_options[:locals]
16
16
  )
17
17
  else
18
- render_erb(path_or_options, locals)
18
+ render_erb(path_or_options, locals)
19
19
  end
20
20
  end
21
21
 
@@ -69,7 +69,11 @@ module Vanity
69
69
  # Generate an HTML report. Outputs to the named file, or stdout with no
70
70
  # arguments.
71
71
  def report(output = nil)
72
- html = render(Vanity.template("report"))
72
+ html = render(Vanity.template("report"),
73
+ :experiments=>Vanity.playground.experiments,
74
+ :experiments_persisted=>Vanity.playground.experiments_persisted?,
75
+ :metrics=>Vanity.playground.metrics
76
+ )
73
77
  if output
74
78
  File.open output, 'w' do |file|
75
79
  file.write html
@@ -460,10 +460,11 @@ module Vanity
460
460
  end
461
461
  end
462
462
 
463
- # Called when tracking associated metric.
463
+ # Called via a hook by the associated metric.
464
464
  def track!(metric_id, timestamp, count, *args)
465
465
  return unless active?
466
466
  identity = identity() rescue nil
467
+ identity ||= args.last[:identity] if args.last.is_a?(Hash) && args.last[:identity]
467
468
  if identity
468
469
  return if connection.ab_showing(@id, identity)
469
470
  index = alternative_for(identity)
@@ -292,10 +292,11 @@ module Vanity
292
292
  # Step 3: Open your browser to http://localhost:3000/vanity
293
293
  module Dashboard
294
294
  def index
295
- @experiments = Vanity.playground.experiments
296
- @experiments_persisted = Vanity.playground.experiments_persisted?
297
- @metrics = Vanity.playground.metrics
298
- render :file=>Vanity.template("_report"), :content_type=>Mime::HTML
295
+ render :file=>Vanity.template("_report"),:content_type=>Mime::HTML, :locals=>{
296
+ :experiments=>Vanity.playground.experiments,
297
+ :experiments_persisted=>Vanity.playground.experiments_persisted?,
298
+ :metrics=>Vanity.playground.metrics
299
+ }
299
300
  end
300
301
 
301
302
  def participant
@@ -52,13 +52,21 @@ module Vanity
52
52
  end
53
53
  end
54
54
 
55
- # Tracks an action associated with a metric.
55
+ # Tracks an action associated with a metric. Useful for calling from a
56
+ # Rack handler. Note that a user should already be added to an experiment
57
+ # via #ab_test before this is called - otherwise, the conversion will be
58
+ # tracked, but the user will not be added to the experiment.
56
59
  #
57
60
  # @example
58
61
  # track! :invitation
62
+ # @example
63
+ # track! :click, { :identity=>Identity.new(env['rack.session']), :values=>[1] }
64
+ #
65
+ # @param count_or_options Defaults to a count of 1. Also accepts a hash
66
+ # of options passed (eventually) to AbTest#track!.
59
67
  # @since 1.2.0
60
- def track!(name, count = 1)
61
- Vanity.playground.track! name, count
68
+ def track!(name, count_or_options = 1)
69
+ Vanity.playground.track! name, count_or_options
62
70
  end
63
71
  end
64
72
  end
@@ -152,12 +152,12 @@ module Vanity
152
152
  when Numeric
153
153
  values = [args]
154
154
  end
155
- identity = Vanity.context.vanity_identity rescue nil
155
+ identity ||= Vanity.context.vanity_identity rescue nil
156
156
  [timestamp || Time.now, identity, values || [1]]
157
157
  end
158
158
  protected :track_args
159
159
 
160
- # Metric definitions use this to introduce tracking hook. The hook is
160
+ # Metric definitions use this to introduce tracking hooks. The hook is
161
161
  # called with metric identifier, timestamp, count and possibly additional
162
162
  # arguments.
163
163
  #
@@ -235,7 +235,7 @@ module Vanity
235
235
 
236
236
  def call_hooks(timestamp, identity, values)
237
237
  @hooks.each do |hook|
238
- hook.call @id, timestamp, values.first || 1
238
+ hook.call @id, timestamp, values.first || 1, :identity=>identity
239
239
  end
240
240
  end
241
241
 
@@ -14,23 +14,23 @@
14
14
  <body>
15
15
  <div class="vanity">
16
16
  <% unless Vanity.playground.collecting? %>
17
- <div class="alert">
17
+ <div class="alert collecting">
18
18
  Vanity is currently not collecting data or metrics. To turn on data collection, set <span style='font-family: courier'>Vanity.playground.collecting = true;</span> in <span style='font-family: courier'>config/environments/[environment].rb</span>.
19
19
  </div>
20
20
  <% end %>
21
21
 
22
- <% if @experiments_persisted %>
23
- <% if @experiments.present? %>
22
+ <% if experiments_persisted %>
23
+ <% if experiments.present? %>
24
24
  <h2>Experiments</h2>
25
- <%= render :file=>Vanity.template("_experiments"), :locals=>{:experiments=>@experiments} %>
25
+ <%= render :file=>Vanity.template("_experiments"), :locals=>{:experiments=>experiments} %>
26
26
  <% end %>
27
27
 
28
- <% unless @metrics.empty? %>
28
+ <% unless metrics.empty? %>
29
29
  <h2>Metrics</h2>
30
- <%= render :file=>Vanity.template("_metrics"), :locals=>{:metrics=>@metrics, :experiments=>@experiments} %>
30
+ <%= render :file=>Vanity.template("_metrics"), :locals=>{:metrics=>metrics, :experiments=>experiments} %>
31
31
  <% end %>
32
32
  <% else %>
33
- <div class="alert">
33
+ <div class="alert persistance">
34
34
  Vanity's cached experiments are out of sync with those on the filesystem and/or those in the datastore. Please restart your server and/or turn on collecting.
35
35
  </div>
36
36
  <% end %>
@@ -1,6 +1,6 @@
1
1
  var httpRequest;
2
2
  <% @_vanity_experiments.each do |name, alternative| %>
3
- var params = "e=<%= name %>&a=<%= alternative.id %>&authenticity_token=" + encodeURIComponent("<%= form_authenticity_token %>");
3
+ var params = "e=<%= name %>&a=<%= alternative.id %>&authenticity_token=<%= CGI.escape(form_authenticity_token) %>";
4
4
  if (window.XMLHttpRequest) { // Mozilla, Safari, ...
5
5
  httpRequest = new XMLHttpRequest();
6
6
  } else if (window.ActiveXObject) { // IE
@@ -1,5 +1,5 @@
1
1
  module Vanity
2
- VERSION = "1.9.0"
2
+ VERSION = "1.9.1"
3
3
 
4
4
  module Version
5
5
  version = VERSION.to_s.split(".").map { |i| i.to_i }
@@ -29,6 +29,12 @@ class RedisAdapterTest < Test::Unit::TestCase
29
29
  [redis_adapter, mocked_redis]
30
30
  end
31
31
 
32
+ def test_connect_to_existing_redis
33
+ mocked_redis = stub("Redis")
34
+ adapter = Vanity::Adapters.redis_connection(:redis => mocked_redis)
35
+ assert_equal mocked_redis, adapter.redis
36
+ end
37
+
32
38
  def test_graceful_failure_metric_track
33
39
  redis_adapter, mocked_redis = stub_redis
34
40
  mocked_redis.stubs(:incrby).raises(RuntimeError)
@@ -225,18 +225,31 @@ class AbTestTest < ActionController::TestCase
225
225
  assert_equal id, experiment(:foobar).playground.connection.ab_assigned(experiment(:foobar).id, "6e98ec")
226
226
  end
227
227
 
228
+ def test_ab_assigned
229
+ identity = { :a => :b }
230
+ new_ab_test :foobar do
231
+ alternatives "foo", "bar"
232
+ identify { identity }
233
+ metrics :coolness
234
+ end
235
+ assert_equal nil, experiment(:foobar).playground.connection.ab_assigned(experiment(:foobar).id, identity)
236
+ assert id = experiment(:foobar).choose.id
237
+ assert_equal id, experiment(:foobar).playground.connection.ab_assigned(experiment(:foobar).id, identity)
238
+ end
239
+
228
240
  # -- Unequal probabilities --
229
241
 
230
242
  def test_returns_the_same_alternative_consistently_when_using_probabilities
231
243
  new_ab_test :foobar do
232
244
  alternatives "foo", "bar"
233
245
  identify { "6e98ec" }
234
- rebalance_frequency 100
246
+ rebalance_frequency 10
235
247
  metrics :coolness
236
248
  end
237
- assert value = experiment(:foobar).choose.value
249
+ value = experiment(:foobar).choose.value
250
+ assert value
238
251
  assert_match /foo|bar/, value
239
- 1000.times do
252
+ 100.times do
240
253
  assert_equal value, experiment(:foobar).choose.value
241
254
  end
242
255
  end
@@ -252,9 +265,9 @@ class AbTestTest < ActionController::TestCase
252
265
  altered_alts[0].probability=30
253
266
  altered_alts[1].probability=70
254
267
  experiment(:foobar).set_alternative_probabilities altered_alts
255
- alts = Array.new(1000) { experiment(:foobar).choose.value }
268
+ alts = Array.new(600) { experiment(:foobar).choose.value }
256
269
  assert_equal %w{bar foo}, alts.uniq.sort
257
- assert_in_delta alts.select { |a| a == altered_alts[0].value }.size, 300, 100 # this may fail, such is propability
270
+ assert_in_delta alts.select { |a| a == altered_alts[0].value }.size, 200, 60 # this may fail, such is propability
258
271
  end
259
272
 
260
273
  # -- Rebalancing probabilities --
@@ -0,0 +1,14 @@
1
+ require "test/test_helper"
2
+
3
+ context "Object#track!" do
4
+ test "identity option sets identity" do
5
+ metric "Coolness"
6
+ new_ab_test :foobar do
7
+ alternatives "foo", "bar"
8
+ metrics :coolness
9
+ end
10
+ track! :coolness, :identity=>'quux', :values=>[2]
11
+
12
+ assert_equal 2, experiment(:foobar).alternatives.sum(&:conversions)
13
+ end
14
+ end
@@ -264,6 +264,7 @@ context "ActiveRecord Metric" do
264
264
  end
265
265
  end
266
266
  Vanity.playground.metrics
267
+ Sky.create!
267
268
  (1..5).each do |height|
268
269
  Sky.create! :height=>height
269
270
  end
@@ -81,7 +81,7 @@ context "Metric tracking" do
81
81
  2.times { Vanity.playground.track! :cheers_sec }
82
82
  yawns = Vanity.playground.metric(:yawns_sec).values(today, today).first
83
83
  cheers = Vanity.playground.metric(:cheers_sec).values(today, today).first
84
- assert yawns = 2 * cheers
84
+ assert yawns == 2 * cheers
85
85
  end
86
86
 
87
87
  test "can tell the time" do
@@ -11,7 +11,7 @@ if !defined?(Rails::Railtie) && ActiveRecord::Base.connected?
11
11
  @original = Vanity.playground.connection
12
12
  File.unlink "test/myapp/config/vanity.yml" rescue nil
13
13
  File.open("test/myapp/config/vanity.yml", "w") do |io|
14
- io.write YAML.dump({ "production"=>DATABASE })
14
+ io.write YAML.dump({ "production"=>DATABASE })
15
15
  end
16
16
  @server = PhusionPassenger::SpawnManager.new
17
17
  @server.start
@@ -23,31 +23,31 @@ class RailsDashboardTest < ActionController::TestCase
23
23
  get :index
24
24
  assert_response :success
25
25
  assert @response.body =~ %r{div class="vanity"}
26
+ assert @response.body =~ %r{<h2>Experiments</h2>}
27
+ assert @response.body =~ %r{<h2>Metrics</h2>}
26
28
  end
27
29
 
28
- def test_assigns_experiments
30
+ def test_index_not_collecting
31
+ Vanity.playground.collecting = false
29
32
  get :index
30
- experiments = assigns(:experiments).with_indifferent_access
31
-
32
- assert experiments.respond_to?(:keys)
33
- assert experiments.keys.include?("food")
34
- assert experiments.values.first.name == :food
33
+ assert_response :success
34
+ assert @response.body =~ %r{<div class="alert collecting">}
35
35
  end
36
36
 
37
- def test_assigns_metrics
38
- get :index
39
- metrics = assigns(:metrics).with_indifferent_access
40
- assert metrics.respond_to?(:keys)
41
- assert metrics.keys.include?("sugar_high")
42
- assert metrics.values.first.name == "sugar_high"
43
- end
37
+ def test_index_not_persisted
38
+ name = 'Price'
39
+ id = :price
40
+ experiment = Vanity::Experiment::AbTest.new(Vanity.playground, id, name)
41
+ Vanity.playground.experiments[id] = experiment
44
42
 
45
- def test_assigns_experiments_persisted
46
43
  get :index
47
- assert assigns(:experiments_persisted)
44
+ assert_response :success
45
+ assert @response.body =~ %r{<div class="alert persistance">}
46
+
47
+ Vanity.playground.experiments.delete(id)
48
48
  end
49
49
 
50
- # -- Actions used in non-admin actions --
50
+ # -- Actions used in non-admin actions, e.g. in JS --
51
51
 
52
52
  def test_add_participant
53
53
  xhr :post, :add_participant, :e => "food", :a => 0
data/test/test_helper.rb CHANGED
@@ -56,8 +56,7 @@ module VanityTestHelpers
56
56
  DATABASE = {
57
57
  "redis"=>"redis://localhost/15",
58
58
  "mongodb"=>"mongodb://localhost/vanity",
59
- "mysql"=> { "adapter"=>"active_record", "active_record_adapter"=>"mysql", "database"=>"vanity_test" },
60
- "postgres"=> { "adapter"=>"active_record", "active_record_adapter"=>"postgresql", "database"=>"vanity_test", "username"=>"postgres" },
59
+ "active_record"=> { "adapter"=>"active_record", "active_record_adapter"=>"sqlite3", "database"=>"vanity_test.sqlite3", "timeout" => 10000, "busy_timeout" => 1000 },
61
60
  "mock"=>"mock:/"
62
61
  }[ENV["DB"]] or raise "No support yet for #{ENV["DB"]}"
63
62
 
@@ -77,18 +76,11 @@ module VanityTestHelpers
77
76
  WebMock.reset!
78
77
  end
79
78
 
80
- # Call this on teardown. It wipes put the playground and any state held in it
81
- # (mostly experiments), resets vanity ID, and clears database of all experiments.
82
- def nuke_playground
83
- Vanity.playground.connection.flushdb
84
- new_playground
85
- end
86
-
87
79
  # Call this if you need a new playground, e.g. to re-define the same experiment,
88
80
  # or reload an experiment (saved by the previous playground).
89
81
  def new_playground
90
82
  Vanity.playground = Vanity::Playground.new(:logger=>$logger, :load_path=>"tmp/experiments")
91
- Vanity.playground.establish_connection DATABASE
83
+ Vanity.playground.establish_connection(DATABASE)
92
84
  end
93
85
 
94
86
  # Defines the specified metrics (one or more names). Returns metric, or array
@@ -151,6 +143,9 @@ if defined?(ActiveSupport::TestCase)
151
143
  class ActiveSupport::TestCase
152
144
  include WebMock::API
153
145
  include VanityTestHelpers
146
+
147
+ self.use_instantiated_fixtures = false if respond_to?(:use_instantiated_fixtures)
148
+ self.use_transactional_fixtures = false if respond_to?(:use_transactional_fixtures)
154
149
  end
155
150
  end
156
151
 
@@ -168,17 +163,17 @@ if defined?(ActionController::TestCase)
168
163
  end
169
164
  end
170
165
 
171
- if ENV["DB"] == "postgres"
172
- ActiveRecord::Base.establish_connection :adapter=>"postgresql", :database=>"vanity_test"
173
- elsif ENV["DB"] == "mysql"
174
- ActiveRecord::Base.establish_connection :adapter=>"mysql", :database=>"vanity_test"
175
- end
176
- ActiveRecord::Base.logger = $logger
166
+ if ENV["DB"] == "active_record"
167
+ connection = {}
168
+ connection[:adapter] = VanityTestHelpers::DATABASE['active_record_adapter']
169
+ connection[:database] = VanityTestHelpers::DATABASE['database']
170
+ ActiveRecord::Base.establish_connection(connection)
171
+ ActiveRecord::Base.logger = $logger
177
172
 
178
- if ENV["DB"] == "mysql" || ENV["DB"] == "postgres"
179
173
  require "generators/templates/vanity_migration"
180
174
  VanityMigration.down rescue nil
181
175
  VanityMigration.up
176
+ ActiveRecord::Base.connection_pool.disconnect!
182
177
  end
183
178
 
184
179
  # test/spec/mini v3
data/vanity.gemspec CHANGED
@@ -22,4 +22,6 @@ Gem::Specification.new do |spec|
22
22
  "--webcvs", "http://github.com/assaf/#{spec.name}"
23
23
 
24
24
  spec.required_ruby_version = ">= 1.8.7"
25
+
26
+ spec.add_development_dependency "bundler", ">= 1.0.0"
25
27
  end
metadata CHANGED
@@ -1,33 +1,41 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: vanity
3
- version: !ruby/object:Gem::Version
4
- hash: 51
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.9.1
5
5
  prerelease:
6
- segments:
7
- - 1
8
- - 9
9
- - 0
10
- version: 1.9.0
11
6
  platform: ruby
12
- authors:
7
+ authors:
13
8
  - Assaf Arkin
14
9
  autorequire:
15
10
  bindir: bin
16
11
  cert_chain: []
17
-
18
- date: 2014-01-28 00:00:00 Z
19
- dependencies: []
20
-
12
+ date: 2014-04-20 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: bundler
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: 1.0.0
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: 1.0.0
21
30
  description: Mirror, mirror on the wall ...
22
31
  email: assaf@labnotes.org
23
- executables:
32
+ executables:
24
33
  - vanity
25
34
  extensions: []
26
-
27
- extra_rdoc_files:
35
+ extra_rdoc_files:
28
36
  - README.rdoc
29
37
  - CHANGELOG
30
- files:
38
+ files:
31
39
  - .autotest
32
40
  - .gitignore
33
41
  - .travis.yml
@@ -149,6 +157,7 @@ files:
149
157
  - test/experiments/metrics/signups.rb
150
158
  - test/experiments/metrics/yawns.rb
151
159
  - test/experiments/null_abc.rb
160
+ - test/helper_test.rb
152
161
  - test/metric/active_record_test.rb
153
162
  - test/metric/base_test.rb
154
163
  - test/metric/google_analytics_test.rb
@@ -167,46 +176,37 @@ files:
167
176
  - test/test_helper.rb
168
177
  - vanity.gemspec
169
178
  homepage: http://vanity.labnotes.org
170
- licenses:
179
+ licenses:
171
180
  - MIT
172
181
  post_install_message: To get started run vanity --help
173
- rdoc_options:
182
+ rdoc_options:
174
183
  - --title
175
- - Vanity 1.9.0
184
+ - Vanity 1.9.1
176
185
  - --main
177
186
  - README.rdoc
178
187
  - --webcvs
179
188
  - http://github.com/assaf/vanity
180
- require_paths:
189
+ require_paths:
181
190
  - lib
182
- required_ruby_version: !ruby/object:Gem::Requirement
191
+ required_ruby_version: !ruby/object:Gem::Requirement
183
192
  none: false
184
- requirements:
185
- - - ">="
186
- - !ruby/object:Gem::Version
187
- hash: 57
188
- segments:
189
- - 1
190
- - 8
191
- - 7
193
+ requirements:
194
+ - - ! '>='
195
+ - !ruby/object:Gem::Version
192
196
  version: 1.8.7
193
- required_rubygems_version: !ruby/object:Gem::Requirement
197
+ required_rubygems_version: !ruby/object:Gem::Requirement
194
198
  none: false
195
- requirements:
196
- - - ">="
197
- - !ruby/object:Gem::Version
198
- hash: 3
199
- segments:
200
- - 0
201
- version: "0"
199
+ requirements:
200
+ - - ! '>='
201
+ - !ruby/object:Gem::Version
202
+ version: '0'
202
203
  requirements: []
203
-
204
204
  rubyforge_project:
205
- rubygems_version: 1.8.25
205
+ rubygems_version: 1.8.23
206
206
  signing_key:
207
207
  specification_version: 3
208
208
  summary: Experience Driven Development framework for Ruby
209
- test_files:
209
+ test_files:
210
210
  - test/adapters/redis_adapter_test.rb
211
211
  - test/autoconnect_test.rb
212
212
  - test/cli_test.rb
@@ -242,6 +242,7 @@ test_files:
242
242
  - test/experiments/metrics/signups.rb
243
243
  - test/experiments/metrics/yawns.rb
244
244
  - test/experiments/null_abc.rb
245
+ - test/helper_test.rb
245
246
  - test/metric/active_record_test.rb
246
247
  - test/metric/base_test.rb
247
248
  - test/metric/google_analytics_test.rb