bucket 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "http://rubygems.org"
2
+ gem "rspec", :require => "spec"
3
+ gem "ruby-debug"
data/README ADDED
@@ -0,0 +1,3 @@
1
+ Bucket
2
+
3
+ Description goes here.
data/Rakefile ADDED
@@ -0,0 +1,50 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'rake/testtask'
4
+ require 'rake/rdoctask'
5
+ require 'rcov/rcovtask'
6
+
7
+ begin
8
+ require 'jeweler'
9
+ Jeweler::Tasks.new do |s|
10
+ s.name = "bucket"
11
+ s.summary = %Q{A/B testing.}
12
+ s.email = "tyler.kovacs@gmail.com"
13
+ s.homepage = "http://github.com/tylerkovacs/bucket"
14
+ s.description = "See README"
15
+ s.authors = ["tylerkovacs"]
16
+ end
17
+ rescue LoadError
18
+ puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
19
+ end
20
+
21
+ Rake::TestTask.new do |t|
22
+ t.libs << 'lib'
23
+ t.pattern = 'test/**/*_test.rb'
24
+ t.verbose = false
25
+ end
26
+
27
+ Rake::RDocTask.new do |rdoc|
28
+ rdoc.rdoc_dir = 'rdoc'
29
+ rdoc.title = 'bucket'
30
+ rdoc.options << '--line-numbers' << '--inline-source'
31
+ rdoc.rdoc_files.include('README*')
32
+ rdoc.rdoc_files.include('lib/**/*.rb')
33
+ end
34
+
35
+ Rcov::RcovTask.new do |t|
36
+ t.libs << 'spec'
37
+ t.test_files = FileList['spec/**/*_spec.rb']
38
+ t.verbose = true
39
+ end
40
+
41
+ task :noop do
42
+ end
43
+
44
+ Rake::TestTask.new(:spec => :noop) do |t|
45
+ t.libs << 'spec'
46
+ t.test_files = FileList['spec/**/*_spec.rb']
47
+ t.verbose = true
48
+ end
49
+
50
+ task :default => :test
data/TODO ADDED
@@ -0,0 +1,52 @@
1
+ Soon
2
+ - convert from plugin to gem
3
+ - put on github and rubygems
4
+
5
+ - general
6
+ - missing config directory shouldn't generate an exception at start
7
+ - measure spec coverage
8
+ - auto detect Rails and apply config hooks
9
+ - set controller filters, etc.
10
+ - apply different Rails config based on Rails version
11
+ - test under rails 2.3 and 3.0
12
+ - test under ruby 1.8.6, 1.8.7 and 1.9
13
+ - documentation
14
+ - comment source code liberally
15
+ - review all comments prior to first ship
16
+ - document all config options (logger, config_path)
17
+ - bucket::test
18
+ - invalid test attribute name should log error
19
+ - or should it just generate an exception?
20
+ - if so, should it be a custom exception?
21
+ - option weighting
22
+ - attribute validation
23
+ - can't have empty options
24
+ - option weights must add up to 100
25
+ - users have persistent assignment to a test
26
+ - users can participate once or multiple times
27
+ - ability to define tests within the view for quick deployment
28
+
29
+
30
+
31
+ config file syntax options
32
+
33
+ bucket_test do
34
+ name 'test 1'
35
+ options [1, 2, 3]
36
+ end
37
+
38
+ bucket_test do
39
+ name 'test 2'
40
+ options {
41
+ 1 => {:weight => 10},
42
+ 2 => {:weight => 50},
43
+ 3 => {:weight => 40}
44
+ }
45
+ end
46
+
47
+ bucket_test do
48
+ name 'test 2'
49
+ options [1, 2, 3]
50
+ weights {1 => 10, 2 => 50, 3 => 40}
51
+ end
52
+
data/init.rb ADDED
@@ -0,0 +1 @@
1
+ require 'bucket'
data/lib/bucket.rb ADDED
@@ -0,0 +1,11 @@
1
+ class Bucket
2
+ include Bucket::Base
3
+ end
4
+
5
+ require "bucket/frameworks/rails" if defined?(Rails)
6
+
7
+ if ENV["BUCKET_ENV"] == "test"
8
+ Bucket.config_path = File.join("spec", "config", "bucket")
9
+ end
10
+
11
+ Bucket.init
@@ -0,0 +1,38 @@
1
+ require 'logger'
2
+
3
+ class Bucket
4
+ module Base
5
+ @@config_path = File.join("config", "bucket")
6
+ @@logger = Logger.new(STDOUT)
7
+
8
+ ACCESSOR_NAMES = [:logger, :config_path]
9
+
10
+ def self.included(base)
11
+ base.extend(ClassMethods)
12
+
13
+ ACCESSOR_NAMES.each do |accessor_name|
14
+ base.class_eval <<-EOF
15
+ def self.#{accessor_name}
16
+ @@#{accessor_name}
17
+ end
18
+
19
+ def self.#{accessor_name}=(value)
20
+ @@#{accessor_name} = value
21
+ end
22
+ EOF
23
+ end
24
+ end
25
+
26
+ module ClassMethods
27
+ def init
28
+ if !File.exists?(config_path)
29
+ logger.error("Bucket configuration directory missing: #{config_path}")
30
+ else
31
+ Dir.glob(File.join(config_path, "test_*")) do |filename|
32
+ Test.from_file(filename)
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,8 @@
1
+ # Default to the Rails logger.
2
+ Bucket.logger = RAILS_DEFAULT_LOGGER
3
+
4
+ # Set config path based on RAILS_ROOT
5
+ Bucket.config_path = File.join(RAILS_ROOT, "config", "bucket")
6
+
7
+ Rails.configuration.after_initialize do
8
+ end
@@ -0,0 +1,75 @@
1
+ class Bucket
2
+ class Test
3
+ # Class variable storing all defined tests.
4
+ @@tests = {}
5
+
6
+ # Bucket::Test DSL
7
+ #
8
+ # The Bucket::Test DSL defines tests.
9
+ #
10
+ # Example:
11
+ # bucket_test do
12
+ # name 'color test'
13
+ # options ['red', 'green', 'blue']
14
+ # end
15
+ #
16
+ # Supported Attributes:
17
+ # name : The name of the test.
18
+ # options : Test options.
19
+ ATTRIBUTE_NAMES = [:name, :options]
20
+
21
+ # Create get/set methods for all methods supported in the DSL.
22
+ ATTRIBUTE_NAMES.each do |attribute_name|
23
+ class_eval <<-EOF
24
+ def #{attribute_name}(value=nil)
25
+ if value
26
+ @attributes['#{attribute_name}'] = value
27
+ else
28
+ @attributes['#{attribute_name}']
29
+ end
30
+ end
31
+ EOF
32
+ end
33
+
34
+ def initialize
35
+ @attributes = {}
36
+ end
37
+
38
+ def choose
39
+ options[rand(options.length)]
40
+ end
41
+
42
+ def option
43
+ @option ||= choose
44
+ end
45
+
46
+ class << self
47
+ def from_file(filename)
48
+ from_string(File.read(filename))
49
+ end
50
+
51
+ def from_string(data)
52
+ instance_eval(data)
53
+ end
54
+
55
+ def bucket_test(options={}, &block)
56
+ Test.add_test(options, &block)
57
+ end
58
+
59
+ def add_test(options={}, &block)
60
+ test = self.new
61
+ test.instance_eval(&block)
62
+ @@tests[test.name] = test
63
+ test
64
+ end
65
+
66
+ def number_of_tests
67
+ @@tests.length
68
+ end
69
+
70
+ def clear!
71
+ @@tests.clear
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,93 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+ require 'tempfile'
3
+
4
+ describe Bucket::Test do
5
+ before(:each) do
6
+ @definition =<<-EOF
7
+ bucket_test do
8
+ name 'test name'
9
+ options [1, 2, 3]
10
+ end
11
+ EOF
12
+ @test = Bucket::Test.from_string(@definition)
13
+ end
14
+
15
+ describe 'attributes' do
16
+ it 'should accept a string for the name attribute' do
17
+ @test.name 'new test name'
18
+ @test.name.should == 'new test name'
19
+ end
20
+
21
+ it 'should accept an array for the options attribute' do
22
+ @test.options [1, 2, 3, 4]
23
+ @test.options.should == [1, 2, 3, 4]
24
+ end
25
+ end
26
+
27
+ describe 'from_string' do
28
+ it 'should set supported attribute' do
29
+ @test.name.should == 'test name'
30
+ @test.options.should == [1, 2, 3]
31
+ end
32
+
33
+ it 'should register the new test' do
34
+ Bucket::Test.number_of_tests.should == 1
35
+ end
36
+ end
37
+
38
+ describe 'from_file' do
39
+ it 'should read the test definition from a file' do
40
+ Bucket::Test.clear!
41
+ Bucket::Test.number_of_tests.should == 0
42
+
43
+ Tempfile.open('from_file') do |file|
44
+ file.write @definition
45
+ file.fsync
46
+
47
+ new_test = Bucket::Test.from_file(file.path)
48
+ new_test.name.should == 'test name'
49
+ new_test.options.should == [1, 2, 3]
50
+ end
51
+
52
+ Bucket::Test.number_of_tests.should == 1
53
+ end
54
+ end
55
+
56
+ describe 'clear!' do
57
+ it 'should remove all registered tests' do
58
+ Bucket::Test.number_of_tests.should == 1
59
+ Bucket::Test.clear!
60
+ Bucket::Test.number_of_tests.should == 0
61
+ end
62
+ end
63
+
64
+ describe 'choose' do
65
+ it 'should choose an option at random' do
66
+ option = @test.choose
67
+ option.should_not be_nil
68
+ @test.options.should include(option)
69
+ end
70
+
71
+ it 'should choose an option using an even distribution' do
72
+ frequencies = Hash.new(0)
73
+ 1000.times { frequencies[@test.choose] += 1}
74
+
75
+ frequencies.values.each do |val|
76
+ val.should be_close(333, 150)
77
+ end
78
+ end
79
+ end
80
+
81
+ describe 'option' do
82
+ it 'should return the selected option' do
83
+ option = @test.option
84
+ option.should_not be_nil
85
+ @test.options.should include(option)
86
+ end
87
+
88
+ it 'should not change between calls' do
89
+ option = @test.option
90
+ 10.times { @test.option.should == option }
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,33 @@
1
+ require "rubygems"
2
+ require "bundler"
3
+ Bundler.setup
4
+
5
+ require 'spec'
6
+ require 'spec/autorun'
7
+ require 'ruby-debug'
8
+
9
+ # This file is copied to ~/spec when you run 'ruby script/generate rspec'
10
+ # from the project root directory.
11
+ ENV["RAILS_ENV"] = "test"
12
+ ENV["BUCKET_ENV"] = "test"
13
+
14
+ require File.dirname(__FILE__) + '/../lib/bucket/base'
15
+ require File.dirname(__FILE__) + '/../lib/bucket/test'
16
+ require File.dirname(__FILE__) + '/../lib/bucket'
17
+
18
+ def load_environment
19
+ require File.expand_path(File.dirname(__FILE__) + "/../config/environment")
20
+ require 'spec/autorun'
21
+ require 'spec/rails'
22
+ require File.expand_path(File.dirname(__FILE__) + "/blueprints")
23
+ require 'lib/test/common_method'
24
+ end
25
+
26
+ Spec::Runner.configure do |config|
27
+ config.before(:each) {
28
+ Bucket::Test.clear!
29
+ }
30
+
31
+ config.before(:all) { }
32
+ config.after(:each) { }
33
+ end
metadata ADDED
@@ -0,0 +1,73 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: bucket
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 0
8
+ - 1
9
+ version: 0.0.1
10
+ platform: ruby
11
+ authors:
12
+ - tylerkovacs
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-07-08 00:00:00 -07:00
18
+ default_executable:
19
+ dependencies: []
20
+
21
+ description: See README
22
+ email: tyler.kovacs@gmail.com
23
+ executables: []
24
+
25
+ extensions: []
26
+
27
+ extra_rdoc_files:
28
+ - README
29
+ files:
30
+ - Gemfile
31
+ - README
32
+ - Rakefile
33
+ - TODO
34
+ - init.rb
35
+ - lib/bucket.rb
36
+ - lib/bucket/base.rb
37
+ - lib/bucket/frameworks/rails.rb
38
+ - lib/bucket/test.rb
39
+ - spec/lib/bucket_test_spec.rb
40
+ - spec/spec_helper.rb
41
+ has_rdoc: true
42
+ homepage: http://github.com/tylerkovacs/bucket
43
+ licenses: []
44
+
45
+ post_install_message:
46
+ rdoc_options:
47
+ - --charset=UTF-8
48
+ require_paths:
49
+ - lib
50
+ required_ruby_version: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ segments:
55
+ - 0
56
+ version: "0"
57
+ required_rubygems_version: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ segments:
62
+ - 0
63
+ version: "0"
64
+ requirements: []
65
+
66
+ rubyforge_project:
67
+ rubygems_version: 1.3.6
68
+ signing_key:
69
+ specification_version: 3
70
+ summary: A/B testing.
71
+ test_files:
72
+ - spec/lib/bucket_test_spec.rb
73
+ - spec/spec_helper.rb