bucket 0.0.1

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