From 9fffd833c1016b902da284b3c9d4ffadc7873bd6 Mon Sep 17 00:00:00 2001 From: Phill Baker Date: Thu, 28 Nov 2013 13:54:54 -0800 Subject: [PATCH] Remove hard Redis gem dependency from gemspec. * Closes #138 * Adds documentation to README. * Moves redis dependency checking to adapter. --- Gemfile | 2 ++ Gemfile.lock | 4 +-- README.rdoc | 22 ++++++++++++++--- gemfiles/rails3.gemfile | 2 ++ gemfiles/rails3.gemfile.lock | 4 +-- gemfiles/rails31.gemfile | 2 ++ gemfiles/rails31.gemfile.lock | 4 +-- gemfiles/rails32.gemfile | 2 ++ gemfiles/rails32.gemfile.lock | 4 +-- gemfiles/rails4.gemfile | 2 ++ gemfiles/rails4.gemfile.lock | 4 +-- lib/vanity.rb | 2 -- lib/vanity/adapters/active_record_adapter.rb | 6 +++-- lib/vanity/adapters/mongodb_adapter.rb | 12 ++++----- lib/vanity/adapters/redis_adapter.rb | 26 ++++++++++++++------ test/adapters/redis_adapter_test.rb | 1 + vanity.gemspec | 2 -- 17 files changed, 69 insertions(+), 32 deletions(-) diff --git a/Gemfile b/Gemfile index cc2c7965..0568f2c6 100644 --- a/Gemfile +++ b/Gemfile @@ -9,6 +9,8 @@ gem "rails", "~>2.3.8" gem "passenger", "~>2.0" # Persistence +gem "redis", ">= 2.1" +gem "redis-namespace", ">= 1.1.0" gem "bson_ext" gem "mongo" gem "mysql" diff --git a/Gemfile.lock b/Gemfile.lock index 95a64a17..3de89ebe 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -2,8 +2,6 @@ PATH remote: . specs: vanity (1.8.3) - redis (>= 2.1) - redis-namespace (>= 1.1.0) GEM remote: https://rubygems.org/ @@ -110,6 +108,8 @@ DEPENDENCIES rack rails (~> 2.3.8) rake + redis (>= 2.1) + redis-namespace (>= 1.1.0) rubystats shoulda timecop diff --git a/README.rdoc b/README.rdoc index b7f89a0d..947a340a 100644 --- a/README.rdoc +++ b/README.rdoc @@ -1,7 +1,7 @@ = Vanity {Build Status}[https://travis-ci.org/assaf/vanity] -Vanity is an Experiment Driven Development framework for Rails. +Vanity is an A/B testing framework for Rails that is datastore agnostic. * All about Vanity: http://vanity.labnotes.org * On Github: http://github.com/assaf/vanity @@ -33,14 +33,30 @@ Add to your Gemfile: ==== Step 1.2 -Choose a method to store experiment results: for Redis or an external tracking mechanism, migrations aren't needed. Mongo is also available. To use a relational database, via ActiveRecord, run the generator and migrations to create the database schema: +Choose a method to store experiment results. + +For Redis: + + gem "redis", ">= 2.1" + gem "redis-namespace", ">= 1.1.0" + +For Mongo: + + gem "bson_ext" + gem "mongo" + +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: + + gem "active_record" + +Fun the generator and migrations to create the database schema: $ rails generate vanity $ rake db:migrate ==== Step 1.3 -add to your application controller: +Turn Vanity on, and pass a reference to a method that identifies a user. For example: class ApplicationController < ActionController::Base use_vanity :current_user diff --git a/gemfiles/rails3.gemfile b/gemfiles/rails3.gemfile index d97046d1..27a2ab9c 100644 --- a/gemfiles/rails3.gemfile +++ b/gemfiles/rails3.gemfile @@ -3,6 +3,8 @@ source "https://rubygems.org" gem "rack" +gem "redis", ">= 2.1" +gem "redis-namespace", ">= 1.1.0" gem "bson_ext" gem "mongo" gem "mysql" diff --git a/gemfiles/rails3.gemfile.lock b/gemfiles/rails3.gemfile.lock index 33b4bde9..97105dee 100644 --- a/gemfiles/rails3.gemfile.lock +++ b/gemfiles/rails3.gemfile.lock @@ -8,8 +8,6 @@ PATH remote: .. specs: vanity (1.8.3) - redis (>= 2.1) - redis-namespace (>= 1.1.0) GEM remote: https://rubygems.org/ @@ -165,6 +163,8 @@ DEPENDENCIES rack rails (= 3.0.11) rake + redis (>= 2.1) + redis-namespace (>= 1.1.0) rubystats shoulda timecop diff --git a/gemfiles/rails31.gemfile b/gemfiles/rails31.gemfile index 673f173a..3f469f88 100644 --- a/gemfiles/rails31.gemfile +++ b/gemfiles/rails31.gemfile @@ -3,6 +3,8 @@ source "https://rubygems.org" gem "rack" +gem "redis", ">= 2.1" +gem "redis-namespace", ">= 1.1.0" gem "bson_ext" gem "mongo" gem "mysql" diff --git a/gemfiles/rails31.gemfile.lock b/gemfiles/rails31.gemfile.lock index 4c054518..a51a90cd 100644 --- a/gemfiles/rails31.gemfile.lock +++ b/gemfiles/rails31.gemfile.lock @@ -8,8 +8,6 @@ PATH remote: .. specs: vanity (1.8.3) - redis (>= 2.1) - redis-namespace (>= 1.1.0) GEM remote: https://rubygems.org/ @@ -174,6 +172,8 @@ DEPENDENCIES rack rails (= 3.1.3) rake + redis (>= 2.1) + redis-namespace (>= 1.1.0) rubystats shoulda timecop diff --git a/gemfiles/rails32.gemfile b/gemfiles/rails32.gemfile index a9f19d8c..0ae5ea08 100644 --- a/gemfiles/rails32.gemfile +++ b/gemfiles/rails32.gemfile @@ -3,6 +3,8 @@ source "https://rubygems.org" gem "rack" +gem "redis", ">= 2.1" +gem "redis-namespace", ">= 1.1.0" gem "bson_ext" gem "mongo" gem "mysql" diff --git a/gemfiles/rails32.gemfile.lock b/gemfiles/rails32.gemfile.lock index 71ce1af2..ddf0563f 100644 --- a/gemfiles/rails32.gemfile.lock +++ b/gemfiles/rails32.gemfile.lock @@ -8,8 +8,6 @@ PATH remote: .. specs: vanity (1.8.3) - redis (>= 2.1) - redis-namespace (>= 1.1.0) GEM remote: https://rubygems.org/ @@ -174,6 +172,8 @@ DEPENDENCIES rack rails (= 3.2.1) rake + redis (>= 2.1) + redis-namespace (>= 1.1.0) rubystats shoulda timecop diff --git a/gemfiles/rails4.gemfile b/gemfiles/rails4.gemfile index 0ab8e934..23e39c91 100644 --- a/gemfiles/rails4.gemfile +++ b/gemfiles/rails4.gemfile @@ -3,6 +3,8 @@ source "https://rubygems.org" gem "rack" +gem "redis", ">= 2.1" +gem "redis-namespace", ">= 1.1.0" gem "bson_ext" gem "mongo" gem "mysql" diff --git a/gemfiles/rails4.gemfile.lock b/gemfiles/rails4.gemfile.lock index e5d0fe78..9a8a3a41 100644 --- a/gemfiles/rails4.gemfile.lock +++ b/gemfiles/rails4.gemfile.lock @@ -8,8 +8,6 @@ PATH remote: .. specs: vanity (1.8.3) - redis (>= 2.1) - redis-namespace (>= 1.1.0) GEM remote: https://rubygems.org/ @@ -172,6 +170,8 @@ DEPENDENCIES rack rails (= 4.0.0) rake + redis (>= 2.1) + redis-namespace (>= 1.1.0) rubystats shoulda timecop diff --git a/lib/vanity.rb b/lib/vanity.rb index 17950cc1..707706fd 100644 --- a/lib/vanity.rb +++ b/lib/vanity.rb @@ -26,8 +26,6 @@ module Vanity require "vanity/experiment/ab_test" # Database adapters require "vanity/adapters/abstract_adapter" -require "vanity/adapters/redis_adapter" -require "vanity/adapters/mongodb_adapter" require "vanity/adapters/mock_adapter" # Playground. require "vanity/playground" diff --git a/lib/vanity/adapters/active_record_adapter.rb b/lib/vanity/adapters/active_record_adapter.rb index 3ed04b11..a5a15fff 100644 --- a/lib/vanity/adapters/active_record_adapter.rb +++ b/lib/vanity/adapters/active_record_adapter.rb @@ -86,6 +86,8 @@ def self.retrieve(experiment, identity, create = true, update_with = nil) end def initialize(options) + + @options = options.inject({}) { |h,kv| h[kv.first.to_s] = kv.last ; h } if @options["active_record_adapter"] && (@options["active_record_adapter"] != "default") @options["adapter"] = @options["active_record_adapter"] @@ -222,8 +224,8 @@ def ab_add_participant(experiment, alternative, identity) # Determines if a participant already has seen this alternative in this experiment. def ab_seen(experiment, identity, alternative) - participant = VanityParticipant.retrieve(experiment, identity, false) - participant && participant.seen == alternative.id + participant = VanityParticipant.retrieve(experiment, identity, false) + participant && participant.seen == alternative.id end # Returns the participant's seen alternative in this experiment, if it exists diff --git a/lib/vanity/adapters/mongodb_adapter.rb b/lib/vanity/adapters/mongodb_adapter.rb index 71d10623..bbb60cd3 100644 --- a/lib/vanity/adapters/mongodb_adapter.rb +++ b/lib/vanity/adapters/mongodb_adapter.rb @@ -73,10 +73,10 @@ def flushdb @experiments.drop @participants.drop end - + # -- Metrics -- - + def get_metric_last_update_at(metric) record = @metrics.find_one(:_id=>metric) record && record["last_update_at"] @@ -99,10 +99,10 @@ def metric_values(metric, from, to) def destroy_metric(metric) @metrics.remove :_id=>metric end - + # -- Experiments -- - + def set_experiment_created_at(experiment, time) @experiments.insert :_id=>experiment, :created_at=>time end @@ -152,8 +152,8 @@ def ab_add_participant(experiment, alternative, identity) # Determines if a participant already has seen this alternative in this experiment. def ab_seen(experiment, identity, alternative) - participant = @participants.find_one({ :experiment=>experiment, :identity=>identity }, { :fields=>[:seen] }) - participant && participant["seen"].first == alternative.id + participant = @participants.find_one({ :experiment=>experiment, :identity=>identity }, { :fields=>[:seen] }) + participant && participant["seen"].first == alternative.id end # Returns the participant's seen alternative in this experiment, if it exists diff --git a/lib/vanity/adapters/redis_adapter.rb b/lib/vanity/adapters/redis_adapter.rb index e30646ca..0fa05c2c 100644 --- a/lib/vanity/adapters/redis_adapter.rb +++ b/lib/vanity/adapters/redis_adapter.rb @@ -5,9 +5,21 @@ class << self # # @since 1.4.0 def redis_connection(spec) + require "redis" + fail "redis >= 2.1 is required" unless valid_redis_version? require "redis/namespace" + fail "redis-namespace >= 1.1.0 is required" unless valid_redis_namespace_version? + RedisAdapter.new(spec) end + + def valid_redis_version? + Gem.loaded_specs['redis'].version >= Gem::Version.create('2.1') + end + + def valid_redis_namespace_version? + Gem.loaded_specs['redis'].version >= Gem::Version.create('1.1.0') + end end # Redis adapter. @@ -60,7 +72,7 @@ def flushdb end # -- Metrics -- - + def get_metric_last_update_at(metric) last_update_at = @metrics["#{metric}:last_update_at"] last_update_at && Time.at(last_update_at.to_i) @@ -84,7 +96,7 @@ def destroy_metric(metric) # -- Experiments -- - + def set_experiment_created_at(experiment, time) @experiments.setnx "#{experiment}:created_at", time.to_i end @@ -131,11 +143,11 @@ def ab_add_participant(experiment, alternative, identity) end def ab_seen(experiment, identity, alternative) - if @experiments.sismember "#{experiment}:alts:#{alternative.id}:participants", identity - return alternative - else - return nil - end + if @experiments.sismember "#{experiment}:alts:#{alternative.id}:participants", identity + return alternative + else + return nil + end end # Returns the participant's seen alternative in this experiment, if it exists diff --git a/test/adapters/redis_adapter_test.rb b/test/adapters/redis_adapter_test.rb index 3b6f5aad..24fda790 100644 --- a/test/adapters/redis_adapter_test.rb +++ b/test/adapters/redis_adapter_test.rb @@ -3,6 +3,7 @@ class RedisAdapterTest < Test::Unit::TestCase def test_warn_on_disconnect_error if defined?(Redis) + require "vanity/adapters/redis_adapter" assert_nothing_raised do Redis.any_instance.stubs(:connect!) mocked_redis = stub("Redis") diff --git a/vanity.gemspec b/vanity.gemspec index 4dbc8563..243abf09 100644 --- a/vanity.gemspec +++ b/vanity.gemspec @@ -22,6 +22,4 @@ Gem::Specification.new do |spec| "--webcvs", "http://github.com/assaf/#{spec.name}" spec.required_ruby_version = ">= 1.8.7" - spec.add_dependency "redis", ">= 2.1" - spec.add_dependency "redis-namespace", ">= 1.1.0" end