a_b_plugin 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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.
@@ -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.
@@ -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
@@ -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"
@@ -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,13 @@
1
+ module ABPlugin
2
+ class API
3
+ include HTTParty
4
+
5
+ def initialize(token)
6
+ @token = token
7
+ end
8
+
9
+ def boot
10
+ self.class.get('/boot.json', :query => { :token => @token })
11
+ end
12
+ end
13
+ end
@@ -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
@@ -0,0 +1 @@
1
+ require File.expand_path(File.dirname(__FILE__) + "/../lib/a_b_plugin")
@@ -0,0 +1 @@
1
+ --color
@@ -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('<', '&lt;').gsub('>', '&gt;')
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
+