a_b_plugin 0.1.0

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