a_b_plugin 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/MIT-LICENSE +18 -0
- data/README.markdown +24 -0
- data/Rakefile +52 -0
- data/gemspec.rb +18 -0
- data/init.rb +1 -0
- data/lib/a_b_plugin.rb +50 -0
- data/lib/a_b_plugin/adapters/rails.rb +23 -0
- data/lib/a_b_plugin/adapters/sinatra.rb +15 -0
- data/lib/a_b_plugin/api.rb +13 -0
- data/lib/a_b_plugin/core_ext/array.rb +14 -0
- data/lib/a_b_plugin/core_ext/module.rb +60 -0
- data/lib/a_b_plugin/helper.rb +17 -0
- data/rails/init.rb +1 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +15 -0
- metadata +78 -0
data/MIT-LICENSE
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
Copyright (c) 2009 Winton Welsh
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
4
|
+
this software and associated documentation files (the "Software"), to deal in
|
5
|
+
the Software without restriction, including without limitation the rights to
|
6
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
7
|
+
the Software, and to permit persons to whom the Software is furnished to do so,
|
8
|
+
subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in all
|
11
|
+
copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
15
|
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
16
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
17
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
18
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.markdown
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
a\_b\_plugin
|
2
|
+
============
|
3
|
+
|
4
|
+
Talk to <code>a_b</code> from your Rails or Sinatra app.
|
5
|
+
|
6
|
+
Install
|
7
|
+
-------
|
8
|
+
|
9
|
+
<pre>
|
10
|
+
sudo gem install a_b_plugin --source http://gemcutter.org
|
11
|
+
</pre>
|
12
|
+
|
13
|
+
Setup
|
14
|
+
-----
|
15
|
+
|
16
|
+
You will need to make a call to <code>ABPlugin.config</code> when your app boots:
|
17
|
+
|
18
|
+
<pre>
|
19
|
+
ABPlugin.config 'kTJkI8e56OisQrexuChW', 'http://ab.mydomain.com'
|
20
|
+
</pre>
|
21
|
+
|
22
|
+
The first parameter is the persistence token from the user you created in <code>a_b</code>.
|
23
|
+
|
24
|
+
The second parameter is the URL to your <code>a_b</code> server.
|
data/Rakefile
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
require 'rake/gempackagetask'
|
4
|
+
require 'spec/rake/spectask'
|
5
|
+
require 'gemspec'
|
6
|
+
|
7
|
+
desc "Generate gemspec"
|
8
|
+
task :gemspec do
|
9
|
+
File.open("#{Dir.pwd}/#{GEM_NAME}.gemspec", 'w') do |f|
|
10
|
+
f.write(GEM_SPEC.to_ruby)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
desc "Install gem"
|
15
|
+
task :install do
|
16
|
+
Rake::Task['gem'].invoke
|
17
|
+
`sudo gem uninstall #{GEM_NAME} -x`
|
18
|
+
`sudo gem install pkg/#{GEM_NAME}*.gem`
|
19
|
+
`rm -Rf pkg`
|
20
|
+
end
|
21
|
+
|
22
|
+
desc "Package gem"
|
23
|
+
Rake::GemPackageTask.new(GEM_SPEC) do |pkg|
|
24
|
+
pkg.gem_spec = GEM_SPEC
|
25
|
+
end
|
26
|
+
|
27
|
+
desc "Rename project"
|
28
|
+
task :rename do
|
29
|
+
name = ENV['NAME'] || File.basename(Dir.pwd)
|
30
|
+
begin
|
31
|
+
dir = Dir['**/gem_template*']
|
32
|
+
from = dir.pop
|
33
|
+
if from
|
34
|
+
rb = from.include?('.rb')
|
35
|
+
to = File.dirname(from) + "/#{name}#{'.rb' if rb}"
|
36
|
+
FileUtils.mv(from, to)
|
37
|
+
end
|
38
|
+
end while dir.length > 0
|
39
|
+
Dir["**/*"].each do |path|
|
40
|
+
next if path.include?('Rakefile')
|
41
|
+
if File.file?(path)
|
42
|
+
`sed -i "" 's/gem_template/#{name}/g' #{path}`
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
desc "Run specs"
|
48
|
+
Spec::Rake::SpecTask.new do |t|
|
49
|
+
t.rcov = true
|
50
|
+
t.spec_opts = ["--format", "specdoc", "--colour"]
|
51
|
+
t.spec_files = FileList["spec/**/*_spec.rb"]
|
52
|
+
end
|
data/gemspec.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
GEM_NAME = 'a_b_plugin'
|
2
|
+
GEM_FILES = FileList['**/*'] - FileList['coverage', 'coverage/**/*', 'pkg', 'pkg/**/*']
|
3
|
+
GEM_SPEC = Gem::Specification.new do |s|
|
4
|
+
# == CONFIGURE ==
|
5
|
+
s.author = "Winton Welsh"
|
6
|
+
s.email = "mail@wintoni.us"
|
7
|
+
s.homepage = "http://github.com/winton/#{GEM_NAME}"
|
8
|
+
s.summary = "Talk to a_b from your Rails or Sinatra app"
|
9
|
+
# == CONFIGURE ==
|
10
|
+
s.add_dependency('httparty', '=0.4.5')
|
11
|
+
s.extra_rdoc_files = [ "README.markdown" ]
|
12
|
+
s.files = GEM_FILES.to_a
|
13
|
+
s.has_rdoc = false
|
14
|
+
s.name = GEM_NAME
|
15
|
+
s.platform = Gem::Platform::RUBY
|
16
|
+
s.require_path = "lib"
|
17
|
+
s.version = "0.1.0"
|
18
|
+
end
|
data/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/rails/init"
|
data/lib/a_b_plugin.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/a_b_plugin/core_ext/array"
|
2
|
+
require File.dirname(__FILE__) + "/a_b_plugin/core_ext/module"
|
3
|
+
require File.dirname(__FILE__) + "/a_b_plugin/api"
|
4
|
+
require File.dirname(__FILE__) + "/a_b_plugin/helper"
|
5
|
+
require File.dirname(__FILE__) + "/a_b_plugin/adapters/rails" if defined?(Rails)
|
6
|
+
require File.dirname(__FILE__) + "/a_b_plugin/adapters/sinatra" if defined?(Sinatra)
|
7
|
+
|
8
|
+
module ABPlugin
|
9
|
+
|
10
|
+
mattr_accessor :api
|
11
|
+
mattr_accessor :session_id
|
12
|
+
mattr_accessor :tests
|
13
|
+
mattr_accessor :token
|
14
|
+
mattr_accessor :url
|
15
|
+
mattr_accessor :user_token
|
16
|
+
|
17
|
+
class <<self
|
18
|
+
|
19
|
+
def config(token, url)
|
20
|
+
API.base_uri @@url
|
21
|
+
@@api = API.new token
|
22
|
+
boot = @@api.boot
|
23
|
+
@@tests = boot['tests']
|
24
|
+
@@token = token
|
25
|
+
@@url = url
|
26
|
+
@@user_token = boot['user_token']
|
27
|
+
end
|
28
|
+
|
29
|
+
def select_variant?(selections, variant)
|
30
|
+
test = test_from_variant(variant)
|
31
|
+
return false unless test
|
32
|
+
if !selections || !selections[test['name']]
|
33
|
+
variants = test['variants'].sort do |a, b|
|
34
|
+
a['visitors'] <=> b['visitors']
|
35
|
+
end
|
36
|
+
variants.first['visitors'] += 1
|
37
|
+
selections[test['name']] = variants.first['name']
|
38
|
+
end
|
39
|
+
selections[test['name']] == variant
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_from_variant(variant)
|
43
|
+
return nil unless ABPlugin.tests
|
44
|
+
tests = ABPlugin.tests.select do |t|
|
45
|
+
t['variants'].collect { |v| v['name'] }.include?(variant)
|
46
|
+
end
|
47
|
+
tests.first
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module ABPlugin
|
2
|
+
module Adapters
|
3
|
+
module Rails
|
4
|
+
|
5
|
+
def self.included(klass)
|
6
|
+
if defined?(::ApplicationController)
|
7
|
+
raise 'Please require a_b_plugin before all other plugins.'
|
8
|
+
end
|
9
|
+
klass.prepend_before_filter :a_b_plugin_before_filter
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def a_b_plugin_before_filter
|
15
|
+
ABPlugin.session_id = session.session_id
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
ActionController::Base.send(:include, ABRails::Adapters::Rails)
|
22
|
+
ActionController::Base.send(:include, ABRails::Helper)
|
23
|
+
ActionController::Base.helper(ABRails::Helper)
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module ABPlugin
|
2
|
+
module Adapters
|
3
|
+
module Sinatra
|
4
|
+
|
5
|
+
def self.included(klass)
|
6
|
+
klass.send :before do
|
7
|
+
ABPlugin.session_id = env['rack.session.options']['_session_id']
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
Sinatra::Base.send(:include, ABPlugin::Adapters::Sinatra)
|
15
|
+
Sinatra::Base.send(:include, ABPlugin::Helper)
|
@@ -0,0 +1,14 @@
|
|
1
|
+
class Array
|
2
|
+
# Extracts options from a set of arguments. Removes and returns the last
|
3
|
+
# element in the array if it's a hash, otherwise returns a blank hash.
|
4
|
+
#
|
5
|
+
# def options(*args)
|
6
|
+
# args.extract_options!
|
7
|
+
# end
|
8
|
+
#
|
9
|
+
# options(1, 2) # => {}
|
10
|
+
# options(1, 2, :a => :b) # => {:a=>:b}
|
11
|
+
def extract_options!
|
12
|
+
last.is_a?(::Hash) ? pop : {}
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
class Module
|
2
|
+
def mattr_reader(*syms)
|
3
|
+
syms.extract_options!
|
4
|
+
syms.each do |sym|
|
5
|
+
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
|
6
|
+
unless defined? @@#{sym} # unless defined? @@pagination_options
|
7
|
+
@@#{sym} = nil # @@pagination_options = nil
|
8
|
+
end # end
|
9
|
+
|
10
|
+
def self.#{sym} # def self.pagination_options
|
11
|
+
@@#{sym} # @@pagination_options
|
12
|
+
end # end
|
13
|
+
|
14
|
+
def #{sym} # def pagination_options
|
15
|
+
@@#{sym} # @@pagination_options
|
16
|
+
end # end
|
17
|
+
EOS
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def mattr_writer(*syms)
|
22
|
+
options = syms.extract_options!
|
23
|
+
syms.each do |sym|
|
24
|
+
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
|
25
|
+
unless defined? @@#{sym} # unless defined? @@pagination_options
|
26
|
+
@@#{sym} = nil # @@pagination_options = nil
|
27
|
+
end # end
|
28
|
+
|
29
|
+
def self.#{sym}=(obj) # def self.pagination_options=(obj)
|
30
|
+
@@#{sym} = obj # @@pagination_options = obj
|
31
|
+
end # end
|
32
|
+
EOS
|
33
|
+
|
34
|
+
unless options[:instance_writer] == false
|
35
|
+
class_eval(<<-EOS, __FILE__, __LINE__)
|
36
|
+
def #{sym}=(obj) # def pagination_options=(obj)
|
37
|
+
@@#{sym} = obj # @@pagination_options = obj
|
38
|
+
end # end
|
39
|
+
EOS
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# Extends the module object with module and instance accessors for class attributes,
|
45
|
+
# just like the native attr* accessors for instance attributes.
|
46
|
+
#
|
47
|
+
# module AppConfiguration
|
48
|
+
# mattr_accessor :google_api_key
|
49
|
+
# self.google_api_key = "123456789"
|
50
|
+
#
|
51
|
+
# mattr_accessor :paypal_url
|
52
|
+
# self.paypal_url = "www.sandbox.paypal.com"
|
53
|
+
# end
|
54
|
+
#
|
55
|
+
# AppConfiguration.google_api_key = "overriding the api key!"
|
56
|
+
def mattr_accessor(*syms)
|
57
|
+
mattr_reader(*syms)
|
58
|
+
mattr_writer(*syms)
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module ABPlugin
|
2
|
+
module Helper
|
3
|
+
|
4
|
+
private
|
5
|
+
|
6
|
+
def a_b(variant, &block)
|
7
|
+
block.call if block_given? && ABPlugin.select_variant?(@a_b_selections, variant)
|
8
|
+
end
|
9
|
+
|
10
|
+
def a_b_script_tag
|
11
|
+
token = Digest::SHA256.hexdigest(ABPlugin.session_id + ABPlugin.user_token)
|
12
|
+
variants = @a_b_selections.values.collect { |v| "variants[]=#{v}" }.join '&'
|
13
|
+
url = ABPlugin.url + "/visit.js?session_id=#{ABPlugin.session_id}&token=#{token}&#{variants}"
|
14
|
+
"<script src=\"#{url}\" type=\"text/javascript\"></script>"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/rails/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + "/../lib/a_b_plugin")
|
data/spec/spec.opts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
$testing = true
|
2
|
+
SPEC = File.dirname(__FILE__)
|
3
|
+
|
4
|
+
require 'pp'
|
5
|
+
require File.expand_path("#{SPEC}/../rails/init")
|
6
|
+
|
7
|
+
Spec::Runner.configure do |config|
|
8
|
+
end
|
9
|
+
|
10
|
+
# For use with rspec textmate bundle
|
11
|
+
def debug(object)
|
12
|
+
puts "<pre>"
|
13
|
+
puts object.pretty_inspect.gsub('<', '<').gsub('>', '>')
|
14
|
+
puts "</pre>"
|
15
|
+
end
|
metadata
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: a_b_plugin
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Winton Welsh
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-11-19 00:00:00 -08:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: httparty
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - "="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 0.4.5
|
24
|
+
version:
|
25
|
+
description:
|
26
|
+
email: mail@wintoni.us
|
27
|
+
executables: []
|
28
|
+
|
29
|
+
extensions: []
|
30
|
+
|
31
|
+
extra_rdoc_files:
|
32
|
+
- README.markdown
|
33
|
+
files:
|
34
|
+
- gemspec.rb
|
35
|
+
- init.rb
|
36
|
+
- lib/a_b_plugin/adapters/rails.rb
|
37
|
+
- lib/a_b_plugin/adapters/sinatra.rb
|
38
|
+
- lib/a_b_plugin/api.rb
|
39
|
+
- lib/a_b_plugin/core_ext/array.rb
|
40
|
+
- lib/a_b_plugin/core_ext/module.rb
|
41
|
+
- lib/a_b_plugin/helper.rb
|
42
|
+
- lib/a_b_plugin.rb
|
43
|
+
- MIT-LICENSE
|
44
|
+
- rails/init.rb
|
45
|
+
- Rakefile
|
46
|
+
- README.markdown
|
47
|
+
- spec/spec.opts
|
48
|
+
- spec/spec_helper.rb
|
49
|
+
has_rdoc: true
|
50
|
+
homepage: http://github.com/winton/a_b_plugin
|
51
|
+
licenses: []
|
52
|
+
|
53
|
+
post_install_message:
|
54
|
+
rdoc_options: []
|
55
|
+
|
56
|
+
require_paths:
|
57
|
+
- lib
|
58
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - ">="
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: "0"
|
63
|
+
version:
|
64
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: "0"
|
69
|
+
version:
|
70
|
+
requirements: []
|
71
|
+
|
72
|
+
rubyforge_project:
|
73
|
+
rubygems_version: 1.3.5
|
74
|
+
signing_key:
|
75
|
+
specification_version: 3
|
76
|
+
summary: Talk to a_b from your Rails or Sinatra app
|
77
|
+
test_files: []
|
78
|
+
|