split_tester 0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/MIT-LICENSE +20 -0
- data/README +39 -0
- data/Rakefile +53 -0
- data/files/split_tests.yml +9 -0
- data/init.rb +1 -0
- data/install.rb +7 -0
- data/lib/generators/USAGE +9 -0
- data/lib/generators/commands.rb +37 -0
- data/lib/generators/split_test_generator.rb +11 -0
- data/lib/generators/split_tester_install_generator.rb +10 -0
- data/lib/generators/templates/split_tests.yml +9 -0
- data/lib/split_tester/caching.rb +14 -0
- data/lib/split_tester/controller.rb +47 -0
- data/lib/split_tester/railtie.rb +22 -0
- data/lib/split_tester/translation_helper.rb +43 -0
- data/lib/split_tester.rb +70 -0
- data/rails/init.rb +3 -0
- data/split_tester.gemspec +16 -0
- data/test/split_tester_test.rb +8 -0
- data/test/test_helper.rb +3 -0
- data/uninstall.rb +2 -0
- metadata +86 -0
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2011 [name of plugin creator]
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
SplitTester
|
2
|
+
===========
|
3
|
+
|
4
|
+
This is a plugin that I wrote for doing split testing in a rails application with Google Analytics as your dashboard. The idea is to create an easy to use bucket testing system that wouldn't require another dashboard just for looking at the data. The logic and system are a simplified version of bucket testing based on what I've learned while bucket testing experiments on Yahoo! Search, one of the highest traffic pages on the internet.
|
5
|
+
|
6
|
+
With this system, you don't need to muddy up your views, controllers or language files. The test elements are kept separate and can be turned on and off via percentage allocations of your traffic. It is a cookie based system, so users will have a consistent experience even if they end their session and return later. You can also run as many tests at the same time as you would like, only limited by the amount of traffic you have.
|
7
|
+
|
8
|
+
I have also built in support for action caching so that you can keep your application fast and awesome.
|
9
|
+
|
10
|
+
The code quality isn't as high as I would like and I have taken some shortcuts. I would love any help or input. :)
|
11
|
+
|
12
|
+
A new test is a made up of a locale.yml file and or a collection of new views. The local file can override any translations that are in use and the views are direct replacements for views in the core app (BASELINE).
|
13
|
+
|
14
|
+
Usage
|
15
|
+
==========
|
16
|
+
|
17
|
+
As a plugin:
|
18
|
+
|
19
|
+
rails plugin install git://github.com/jhubert/rails-split-tester.git
|
20
|
+
|
21
|
+
As a gem:
|
22
|
+
|
23
|
+
Place this in your Gemfile:
|
24
|
+
|
25
|
+
gem 'split_tester', :git => 'git://github.com/jhubert/rails-split-tester.git'
|
26
|
+
|
27
|
+
then run:
|
28
|
+
|
29
|
+
bundle install
|
30
|
+
|
31
|
+
and then run the generator:
|
32
|
+
|
33
|
+
rails g split_tester_install
|
34
|
+
|
35
|
+
A file named split_tests.yml will be created in your config folder. This is where you will define the tests you want to run and what percentage of the traffic they should receive.
|
36
|
+
|
37
|
+
A folder named "split" will be created in your test folder. This is where the actual configuration of the split tests will go.
|
38
|
+
|
39
|
+
Please see my demo app for an example: https://github.com/jhubert/split-tested-app-demo
|
data/Rakefile
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
require 'rake/testtask'
|
4
|
+
require 'rake/gempackagetask'
|
5
|
+
require 'rake/rdoctask'
|
6
|
+
|
7
|
+
desc 'Default: run unit tests.'
|
8
|
+
task :default => :test
|
9
|
+
|
10
|
+
desc 'Test the split_tester plugin.'
|
11
|
+
Rake::TestTask.new(:test) do |t|
|
12
|
+
t.libs << 'lib'
|
13
|
+
t.libs << 'test'
|
14
|
+
t.pattern = 'test/**/*_test.rb'
|
15
|
+
t.verbose = true
|
16
|
+
end
|
17
|
+
|
18
|
+
desc 'Generate documentation for the split_tester plugin.'
|
19
|
+
Rake::RDocTask.new(:rdoc) do |rdoc|
|
20
|
+
rdoc.rdoc_dir = 'rdoc'
|
21
|
+
rdoc.title = 'SplitTester'
|
22
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
23
|
+
rdoc.rdoc_files.include('README')
|
24
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
25
|
+
end
|
26
|
+
|
27
|
+
PKG_FILES = FileList[
|
28
|
+
'[a-zA-Z]*',
|
29
|
+
'lib/**/*',
|
30
|
+
'rails/**/*',
|
31
|
+
'files/*',
|
32
|
+
'test/**/*'
|
33
|
+
]
|
34
|
+
|
35
|
+
spec = Gem::Specification.new do |s|
|
36
|
+
s.name = "split_tester"
|
37
|
+
s.version = "0.3"
|
38
|
+
s.author = "Jeremy Hubert"
|
39
|
+
s.email = "jhubert@gmail.com"
|
40
|
+
s.homepage = "http://jeremyhubert.com/"
|
41
|
+
s.platform = Gem::Platform::RUBY
|
42
|
+
s.summary = "Provides A/B split testing functionality for Rails"
|
43
|
+
s.description = "Split Tester provides support for A/B Split testing your pages with integration into Google Analytics."
|
44
|
+
s.files = PKG_FILES.to_a
|
45
|
+
s.require_path = "lib"
|
46
|
+
s.has_rdoc = false
|
47
|
+
s.extra_rdoc_files = ["README"]
|
48
|
+
end
|
49
|
+
|
50
|
+
desc 'Turn this plugin into a gem.'
|
51
|
+
Rake::GemPackageTask.new(spec) do |pkg|
|
52
|
+
pkg.gem_spec = spec
|
53
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
# Size represents the percent of traffic you want for each test
|
2
|
+
# The total of all sizes should equal 100% of traffic.
|
3
|
+
# It's easiest to think of it as being based out of 10 or 100.
|
4
|
+
BASELINE:
|
5
|
+
description: The baseline. This is a version running the default code.
|
6
|
+
size: 10
|
7
|
+
FirstTest:
|
8
|
+
description: This is a sample test. Replace this with your actual test.
|
9
|
+
size: 0
|
data/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "lib", "split_tester")
|
data/install.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'rails_generator'
|
2
|
+
require 'rails_generator/commands'
|
3
|
+
|
4
|
+
module SplitTester #:nodoc:
|
5
|
+
module Generator #:nodoc:
|
6
|
+
module Commands #:nodoc:
|
7
|
+
module Create
|
8
|
+
def setup
|
9
|
+
file("split_tests.yml", "definition.txt")
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
module Destroy
|
14
|
+
def setup
|
15
|
+
file("split_tests.yml", "definition.txt")
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
module List
|
20
|
+
def setup
|
21
|
+
file("split_tests.yml", "definition.txt")
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
module Update
|
26
|
+
def setup
|
27
|
+
file("split_tests.yml", "definition.txt")
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
Rails::Generator::Commands::Create.send :include, SplitTester::Generator::Commands::Create
|
35
|
+
Rails::Generator::Commands::Destroy.send :include, SplitTester::Generator::Commands::Destroy
|
36
|
+
Rails::Generator::Commands::List.send :include, SplitTester::Generator::Commands::List
|
37
|
+
Rails::Generator::Commands::Update.send :include, SplitTester::Generator::Commands::Update
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'rails/generators'
|
2
|
+
|
3
|
+
class SplitTestGenerator < Rails::Generators::NamedBase
|
4
|
+
argument :size, :type => :string, :default => '0', :banner => "size"
|
5
|
+
argument :description, :type => :string, :default => 'Your Test Description', :banner => "description"
|
6
|
+
source_root File.expand_path('../templates', __FILE__)
|
7
|
+
|
8
|
+
def manifest
|
9
|
+
append_to_file('config/split_tests.yml', "#{name}:\n description: #{description}\n size: #{size}\n")
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
require 'rails/generators'
|
2
|
+
|
3
|
+
class SplitTesterInstallGenerator < Rails::Generators::Base
|
4
|
+
source_root File.expand_path('../templates', __FILE__)
|
5
|
+
|
6
|
+
def manifest
|
7
|
+
empty_directory('test/split/')
|
8
|
+
template('split_tests.yml', 'config/split_tests.yml')
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
# Size represents the percent of traffic you want for each test
|
2
|
+
# The total of all sizes should equal 100% of traffic.
|
3
|
+
# It's easiest to think of it as being based out of 10 or 100.
|
4
|
+
BASELINE:
|
5
|
+
description: The baseline. This is a version running the default code.
|
6
|
+
size: 10
|
7
|
+
FirstTest:
|
8
|
+
description: This is a sample test. Replace this with your actual test.
|
9
|
+
size: 0
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# Change the namespace for caching if the current request
|
2
|
+
# is a split test so that caches don't get mixed together
|
3
|
+
module SplitTester #:nodoc:
|
4
|
+
module Caching
|
5
|
+
def self.included(base)
|
6
|
+
base.class_eval {
|
7
|
+
def fragment_cache_key(key)
|
8
|
+
namespace = is_split_test? ? "views-split-#{current_split_test_key}" : :views
|
9
|
+
ActiveSupport::Cache.expand_cache_key(key.is_a?(Hash) ? url_for(key).split("://").last : key, namespace)
|
10
|
+
end
|
11
|
+
}
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module SplitTester #:nodoc:
|
2
|
+
module Controller
|
3
|
+
def self.included(base)
|
4
|
+
base.send(:include, InstanceMethods)
|
5
|
+
base.class_eval {
|
6
|
+
helper_method :is_split_test?, :current_split_test_key
|
7
|
+
|
8
|
+
before_filter :setup_split_testing
|
9
|
+
}
|
10
|
+
end
|
11
|
+
|
12
|
+
module InstanceMethods
|
13
|
+
# If a split_test_key other than BASELINE exists, add the proper
|
14
|
+
# view path to the load paths used by ActionView
|
15
|
+
def setup_split_testing
|
16
|
+
return unless is_split_test?
|
17
|
+
split_test_path = SplitTester::Base.view_path(current_split_test_key)
|
18
|
+
prepend_view_path(split_test_path) if split_test_path
|
19
|
+
end
|
20
|
+
|
21
|
+
# Get the existing split_test_key from the session or the cookie.
|
22
|
+
# If there isn't one, or if the one isn't a running test anymore
|
23
|
+
# assign the user a new key and store it.
|
24
|
+
# Don't assign a key if it is a crawler. (This doesn't feel right)
|
25
|
+
def get_split_test_key
|
26
|
+
return params[:force_test_key] if params[:force_test_key] && SplitTester::Base.active_test?(params[:force_test_key]) # just for testing
|
27
|
+
return session[:split_test_key] if session[:split_test_key] && SplitTester::Base.active_test?(session[:split_test_key])
|
28
|
+
return session[:split_test_key] = cookies[:split_test_key] if cookies[:split_test_key] && SplitTester::Base.active_test?(cookies[:split_test_key])
|
29
|
+
if (request.user_agent =~ /\b(Baidu|Gigabot|Googlebot|libwww-perl|lwp-trivial|msnbot|SiteUptime|Slurp|WordPress|ZIBB|ZyBorg)\b/i)
|
30
|
+
session[:split_test_key] = nil
|
31
|
+
else
|
32
|
+
session[:split_test_key] = SplitTester::Base.random_test_key
|
33
|
+
cookies[:split_test_key] = session[:split_test_key]
|
34
|
+
end
|
35
|
+
return session[:split_test_key]
|
36
|
+
end
|
37
|
+
|
38
|
+
def current_split_test_key
|
39
|
+
@split_test_key ||= get_split_test_key
|
40
|
+
end
|
41
|
+
|
42
|
+
def is_split_test?
|
43
|
+
current_split_test_key && current_split_test_key != 'BASELINE'
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'split_tester'
|
2
|
+
|
3
|
+
module SplitTester
|
4
|
+
if defined? Rails::Railtie
|
5
|
+
require 'rails'
|
6
|
+
class Railtie < Rails::Railtie
|
7
|
+
initializer "split_tester.init" do
|
8
|
+
SplitTester::Railtie.insert
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class Railtie
|
14
|
+
def self.insert
|
15
|
+
ActionController::Base.send(:include, SplitTester::Controller)
|
16
|
+
ActionController::Caching::Fragments.send(:include, SplitTester::Caching)
|
17
|
+
ActionView::Helpers::TranslationHelper.send(:include, SplitTester::TranslationHelper)
|
18
|
+
|
19
|
+
SplitTester::Base.setup
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'action_view'
|
2
|
+
|
3
|
+
module SplitTester
|
4
|
+
module TranslationHelper
|
5
|
+
def self.included(base)
|
6
|
+
# Using class_eval here because base.send(:include, InstanceMethods)
|
7
|
+
# wasn't overwriting the translate method properly. Not sure why.
|
8
|
+
base.class_eval {
|
9
|
+
def translate(key, options = {})
|
10
|
+
key = scope_key_by_partial(key)
|
11
|
+
if is_split_test?
|
12
|
+
# normalize the parameters so that we can add in
|
13
|
+
# the current_split_test_key properly
|
14
|
+
scope = options.delete(:scope)
|
15
|
+
keys = I18n.normalize_keys(I18n.locale, key, scope)
|
16
|
+
keys.shift
|
17
|
+
key = keys.join('.')
|
18
|
+
|
19
|
+
# Set the standard key as a default to fall back on automatically
|
20
|
+
if options[:default]
|
21
|
+
options[:default] = [options[:default]] unless options[:default].is_a?(Array)
|
22
|
+
options[:default].unshift(key.to_sym)
|
23
|
+
else
|
24
|
+
options[:default] = [key.to_sym]
|
25
|
+
end
|
26
|
+
|
27
|
+
key = "#{current_split_test_key}.#{key}"
|
28
|
+
end
|
29
|
+
translation = I18n.translate(key, options.merge!(:raise => true))
|
30
|
+
if html_safe_translation_key?(key) && translation.respond_to?(:html_safe)
|
31
|
+
translation.html_safe
|
32
|
+
else
|
33
|
+
translation
|
34
|
+
end
|
35
|
+
rescue I18n::MissingTranslationData => e
|
36
|
+
keys = I18n.normalize_keys(e.locale, e.key, e.options[:scope])
|
37
|
+
content_tag('span', keys.join(', '), :class => 'translation_missing')
|
38
|
+
end
|
39
|
+
alias t translate
|
40
|
+
}
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
data/lib/split_tester.rb
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'split_tester/railtie'
|
2
|
+
require 'split_tester/controller'
|
3
|
+
require 'split_tester/translation_helper'
|
4
|
+
require 'split_tester/caching'
|
5
|
+
|
6
|
+
module SplitTester
|
7
|
+
class Base
|
8
|
+
# Doesn't have access to Rails.root here
|
9
|
+
begin
|
10
|
+
SPLIT_TESTS = YAML.load_file("config/split_tests.yml")
|
11
|
+
SPLIT_TESTS.keys
|
12
|
+
LOADED = true
|
13
|
+
rescue LoadError
|
14
|
+
puts "[SplitTester] Missing config/split_tests.yml"
|
15
|
+
LOADED = false
|
16
|
+
rescue
|
17
|
+
puts "[SplitTester] Invalid config/split_tests.yml"
|
18
|
+
LOADED = false
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.setup
|
22
|
+
if LOADED
|
23
|
+
# Add the split test language files to the load path
|
24
|
+
I18n.load_path += Dir[Rails.root.join('test', 'split', '*', 'locale.{rb,yml}')]
|
25
|
+
|
26
|
+
@@preprocessed_pathsets = begin
|
27
|
+
SPLIT_TESTS.keys.reject { |k| k == 'BASELINE' }.inject({}) do |pathsets, slug|
|
28
|
+
path = custom_view_path(slug)
|
29
|
+
pathsets[path] = ActionView::Base.process_view_paths(path).first
|
30
|
+
pathsets
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
@@split_test_map = begin
|
35
|
+
tm = {} # test map
|
36
|
+
SPLIT_TESTS.each { |k, v| tm[k] = v['size'].to_i }
|
37
|
+
tm.keys.zip(tm.values).collect { |v,d| (0...d).collect { v }}.flatten
|
38
|
+
end
|
39
|
+
else
|
40
|
+
@@split_test_map = []
|
41
|
+
@@preprocessed_pathsets = []
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.split_test_map
|
46
|
+
@@split_test_map
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.preprocessed_pathsets
|
50
|
+
@@preprocessed_pathsets
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.custom_view_path(name)
|
54
|
+
name == "views" ? "app/views" : "test/split/#{name}/views"
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.active_test?(key)
|
58
|
+
return false unless LOADED
|
59
|
+
SPLIT_TESTS.has_key?(key)
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.view_path(key)
|
63
|
+
preprocessed_pathsets[custom_view_path(key)]
|
64
|
+
end
|
65
|
+
|
66
|
+
def self.random_test_key
|
67
|
+
split_test_map.sample
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
data/rails/init.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.platform = Gem::Platform::RUBY
|
3
|
+
s.name = 'split_tester'
|
4
|
+
s.version = '0.3'
|
5
|
+
s.summary = 'Provides A/B split testing functionality for Rails'
|
6
|
+
s.description = 'Split Tester provides support for A/B Split testing your pages with integration into Google Analytics.'
|
7
|
+
|
8
|
+
s.required_ruby_version = '>= 1.8.7'
|
9
|
+
s.required_rubygems_version = ">= 1.3.6"
|
10
|
+
|
11
|
+
s.author = 'Jeremy Hubert'
|
12
|
+
s.email = 'jhubert@gmail.com'
|
13
|
+
s.homepage = 'http://www.jeremyhubert.com'
|
14
|
+
|
15
|
+
s.add_dependency('rails', '>=3.0.0')
|
16
|
+
end
|
data/test/test_helper.rb
ADDED
data/uninstall.rb
ADDED
metadata
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: split_tester
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 13
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 3
|
9
|
+
version: "0.3"
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Jeremy Hubert
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2011-01-25 00:00:00 -08:00
|
18
|
+
default_executable:
|
19
|
+
dependencies: []
|
20
|
+
|
21
|
+
description: Split Tester provides support for A/B Split testing your pages with integration into Google Analytics.
|
22
|
+
email: jhubert@gmail.com
|
23
|
+
executables: []
|
24
|
+
|
25
|
+
extensions: []
|
26
|
+
|
27
|
+
extra_rdoc_files:
|
28
|
+
- README
|
29
|
+
files:
|
30
|
+
- init.rb
|
31
|
+
- install.rb
|
32
|
+
- MIT-LICENSE
|
33
|
+
- Rakefile
|
34
|
+
- README
|
35
|
+
- split_tester.gemspec
|
36
|
+
- uninstall.rb
|
37
|
+
- lib/generators/commands.rb
|
38
|
+
- lib/generators/split_test_generator.rb
|
39
|
+
- lib/generators/split_tester_install_generator.rb
|
40
|
+
- lib/generators/templates/split_tests.yml
|
41
|
+
- lib/generators/USAGE
|
42
|
+
- lib/split_tester/caching.rb
|
43
|
+
- lib/split_tester/controller.rb
|
44
|
+
- lib/split_tester/railtie.rb
|
45
|
+
- lib/split_tester/translation_helper.rb
|
46
|
+
- lib/split_tester.rb
|
47
|
+
- rails/init.rb
|
48
|
+
- files/split_tests.yml
|
49
|
+
- test/split_tester_test.rb
|
50
|
+
- test/test_helper.rb
|
51
|
+
has_rdoc: true
|
52
|
+
homepage: http://jeremyhubert.com/
|
53
|
+
licenses: []
|
54
|
+
|
55
|
+
post_install_message:
|
56
|
+
rdoc_options: []
|
57
|
+
|
58
|
+
require_paths:
|
59
|
+
- lib
|
60
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
61
|
+
none: false
|
62
|
+
requirements:
|
63
|
+
- - ">="
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
hash: 3
|
66
|
+
segments:
|
67
|
+
- 0
|
68
|
+
version: "0"
|
69
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
70
|
+
none: false
|
71
|
+
requirements:
|
72
|
+
- - ">="
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
hash: 3
|
75
|
+
segments:
|
76
|
+
- 0
|
77
|
+
version: "0"
|
78
|
+
requirements: []
|
79
|
+
|
80
|
+
rubyforge_project:
|
81
|
+
rubygems_version: 1.3.7
|
82
|
+
signing_key:
|
83
|
+
specification_version: 3
|
84
|
+
summary: Provides A/B split testing functionality for Rails
|
85
|
+
test_files: []
|
86
|
+
|