mp-gwo 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/MIT-LICENSE +20 -0
- data/README.markdown +127 -0
- data/Rakefile +56 -0
- data/VERSION +1 -0
- data/gwo.gemspec +16 -0
- data/lib/gwo.rb +235 -0
- data/rails/init.rb +2 -0
- data/spec/lib/gwo_spec.rb +265 -0
- data/spec/spec_helper.rb +4 -0
- metadata +141 -0
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Made by Many
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.markdown
ADDED
@@ -0,0 +1,127 @@
|
|
1
|
+
NOTE: CURRENTLY THIS WORKS ONLY WITH HAML ... I HAVE TO FIX IT IN ORDER TO GET IT RUNNING WITH ERB
|
2
|
+
|
3
|
+
|
4
|
+
This is a Ruby on Rails plugin (gem) which simplifies Server-Side Dynamic Section Variations with
|
5
|
+
[Google Website Optimizer](http://www.google.com/websiteoptimizer) as described in the article
|
6
|
+
[Server-Side Dynamic Section Variations on gwotricks.com](http://www.gwotricks.com/2009/05/server-side-dynamic-section-variations.html)
|
7
|
+
|
8
|
+
== Features:
|
9
|
+
------------
|
10
|
+
|
11
|
+
* very good Ruby on Rails integration
|
12
|
+
* very simple to use
|
13
|
+
* support for several sections that should have different variants
|
14
|
+
* support for named and numbered sections
|
15
|
+
* include part of a page in more than one variant
|
16
|
+
* support for google analytics tracking
|
17
|
+
* kill-switch
|
18
|
+
* well tested
|
19
|
+
|
20
|
+
|
21
|
+
The 'Usage' may look long - but it's fairly straightforward and shouldn't take 5 mins.
|
22
|
+
|
23
|
+
Daniel Bornkessel gwo@moviepilot.com & Alex MacCaw - alex@madebymany.co.uk (original author)
|
24
|
+
|
25
|
+
== Usage
|
26
|
+
--------
|
27
|
+
|
28
|
+
To use GWO, you need two pages:
|
29
|
+
* A test page containing the multi variant sections
|
30
|
+
* A page that signifies conversion (i.e. account creation page)
|
31
|
+
|
32
|
+
[Signup for Google Website Optimizer and](http://www.google.com/websiteoptimizer):
|
33
|
+
|
34
|
+
1. Click create another experiment
|
35
|
+
2. Click multivariate experiment
|
36
|
+
3. Name it and enter the test/conversion urls (if the test will be included on multiple pages (/movies/:id), just choose one of the urls) ... actually the urls you type in don't really matter)
|
37
|
+
4. Select 'You will install and validate the JavaScript tags'
|
38
|
+
5. Ignore the scripts that are offered, but strip out your account id (uacct) and test id (both to be found in the Tracking Script).
|
39
|
+
They look like as follows:
|
40
|
+
var pageTracker=_gat._getTracker("UI-6882082-1");
|
41
|
+
pageTracker._trackPageview("/1662461989/test");
|
42
|
+
So, in this example the uacct is 'UA-6882082-1' and the test id is 1662461989.
|
43
|
+
6. Leave google and prepare your source code
|
44
|
+
7. Add the gwo_experiment tag around the code that is supposed to contain the variants (see the gwo_experiment documentation for
|
45
|
+
possible options)
|
46
|
+
8. in the gwo_experiment you can specify one or more sections your side can contain. Each section can have several
|
47
|
+
variants. So if you have for example 2 sections with each having 3 variants you would have 6 different possible
|
48
|
+
combinations on your page.
|
49
|
+
9. Create your gwo_sections, as in the example. The first parameter is the name of the section,
|
50
|
+
the second the name of the variant(s) in which the following code should be shown (see example code).
|
51
|
+
You can mix variants by just passing in more than one identifier. The
|
52
|
+
original variant has the reserved identifier :original (or 0 if you use numbers)
|
53
|
+
10. The variants can either be identified by numbers (starting at 0 for the original variant) or be named (see below how to
|
54
|
+
assign the names in the google web interface).
|
55
|
+
11. Add a gwo_conversion helper tag on your conversion page passing in your uacct and test id.
|
56
|
+
12. In order to validate the pages in the goole web interface, start rails, surf to the pages (variant and conversion page) you
|
57
|
+
just created and save each one locally.
|
58
|
+
13. back in the google web interface, validate your pages by using the 'offline validation' link and upload the two
|
59
|
+
pages you just saved
|
60
|
+
14. as a next step, define the sections. If you used named identifiers in you rails source code, put the the identifiers
|
61
|
+
name as the content of the variations in the web interface (i.e. the example below would have two variants (+ the original
|
62
|
+
variant); one variation would have the CONTENT (subject & name of the variants are not important) 'minimalistic' and the other
|
63
|
+
'with_sidebar_and_top_signup_box' (without the quotes)). If you use numbered identifiers, just create new variations and leave the
|
64
|
+
content empty.
|
65
|
+
15. finish up and start the experiment
|
66
|
+
16. lean back and let google collect data for you for the next few days ... go back an be shocked about the little number of
|
67
|
+
conversions you will probably get ;)
|
68
|
+
|
69
|
+
|
70
|
+
== Example
|
71
|
+
----------
|
72
|
+
... in haml:
|
73
|
+
|
74
|
+
= gwo_experiment("1662461989", "UA-6882082-1", :signup_box_test, :conditions => (signed_up? && country == "de")) do
|
75
|
+
= gwo_section(:signup_box_test, [:with_sidebar_and_top_signup_box, :minimalistic], :ga_tracking => true, :conditions => (signed_up? && country == "de")) do
|
76
|
+
= render :partial => 'gossib/signup'
|
77
|
+
%span I am only visible in the variants :with_sidebar_and_top_signup_box and :minimalistic
|
78
|
+
|
79
|
+
= gwo_section(:signup_box_test, [:original, :with_sidebar_and_top_signup_box], :conditions => (signed_up? && country == "de")) do
|
80
|
+
= render :partial => 'gossib/bookmark_menu'
|
81
|
+
= render :partial => 'gossip/pics', :locals => {:images => @article.images}
|
82
|
+
.box#
|
83
|
+
%span Hi hi ... I am not visible in :minimalistic
|
84
|
+
|
85
|
+
%span I am visible in every variation
|
86
|
+
|
87
|
+
= gwo_section(:signup_box_test, :original, :conditions => (signed_up? && country == "de")) do
|
88
|
+
%span I am only in the original page
|
89
|
+
|
90
|
+
... or in erb:
|
91
|
+
|
92
|
+
<% gwo_experiment("1662461989", "UA-6882082-1", :signup_box_test, :conditions => (signed_up? && country == "de")) do %>
|
93
|
+
<% render :partial => 'gossip/article.html.haml', :object => @article %>
|
94
|
+
|
95
|
+
<% gwo_section(:signup_box_test, [:with_sidebar_and_top_signup_box, :minimalistic], :conditions => (signed_up? && country == "de")) do %>
|
96
|
+
<%= render :partial => 'gossib/signup' %>
|
97
|
+
<span> I am only visible in the variants :with_sidebar_and_top_signup_box and :minimalistic</span>
|
98
|
+
<% end %>
|
99
|
+
|
100
|
+
<% gwo_section(:signup_box_test, [:original, :with_sidebar_and_top_signup_box], :conditions => (signed_up? && country == "de")) do %>
|
101
|
+
<%= render :partial => 'gossib/bookmark_menu' %>
|
102
|
+
<%= render :partial => 'gossip/pics', :locals => {:images => @article.images} %>
|
103
|
+
<div class="box">
|
104
|
+
<span> Hi hi ... I am not visible in :minimalistic</span>
|
105
|
+
<% end %>
|
106
|
+
|
107
|
+
<span> I am visible in every variation</span>
|
108
|
+
|
109
|
+
<% gwo_section(:signup_box_test, :original, :conditions => (signed_up? && country == "de")) do %>
|
110
|
+
<span> I am only in the original page</span>
|
111
|
+
<% end %>
|
112
|
+
<% end %>
|
113
|
+
|
114
|
+
|
115
|
+
== Conversion page:
|
116
|
+
------------------
|
117
|
+
... haml:
|
118
|
+
|
119
|
+
= gwo_conversion('1909920434', 'UA-23902382-1')
|
120
|
+
|
121
|
+
... erb:
|
122
|
+
|
123
|
+
<%= gwo_conversion('1909920434', 'UA-23902382-1') %>
|
124
|
+
|
125
|
+
|
126
|
+
|
127
|
+
Copyright (c) 2009 Made by Many, Moviepilot, released under the MIT license
|
data/Rakefile
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/testtask'
|
3
|
+
require 'rake/rdoctask'
|
4
|
+
require 'spec/rake/spectask'
|
5
|
+
require 'bundler'
|
6
|
+
|
7
|
+
desc 'Default: run unit tests.'
|
8
|
+
task :default => :rspec
|
9
|
+
|
10
|
+
# ... we are too stupid to get this running
|
11
|
+
#desc "Run all examples"
|
12
|
+
#Spec::Rake::SpecTask.new('rspec') do |t|
|
13
|
+
# t.spec_files = FileList['spec/**/*_spec.rb']
|
14
|
+
#end
|
15
|
+
|
16
|
+
desc "Run rspec tests"
|
17
|
+
task :rspec do
|
18
|
+
system "spec spec"
|
19
|
+
end
|
20
|
+
|
21
|
+
desc 'Generate documentation for the gwo plugin.'
|
22
|
+
Rake::RDocTask.new(:rdoc) do |rdoc|
|
23
|
+
rdoc.rdoc_dir = 'doc'
|
24
|
+
rdoc.title = 'Gwo'
|
25
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
26
|
+
rdoc.rdoc_files.include('README.markdown')
|
27
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
28
|
+
end
|
29
|
+
|
30
|
+
desc 'Install latest gem'
|
31
|
+
task :install_only do
|
32
|
+
system "gem install $( ls -t -1 *.gem | head -n 1 )"
|
33
|
+
end
|
34
|
+
|
35
|
+
desc 'Build and install gem'
|
36
|
+
task :install do
|
37
|
+
system "gem build gwo.gemspec"
|
38
|
+
system "gem install $( ls -t -1 *.gem | head -n 1 )"
|
39
|
+
end
|
40
|
+
|
41
|
+
require 'jeweler'
|
42
|
+
Jeweler::Tasks.new do |gem|
|
43
|
+
# gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
|
44
|
+
gem.name = "mp-gwo"
|
45
|
+
gem.homepage = "http://github.com/moviepilot/gwo"
|
46
|
+
gem.license = "MIT"
|
47
|
+
gem.summary = %Q{Google Website Optimizer Helper}
|
48
|
+
gem.description = %Q{Google Website Optimizer Helper}
|
49
|
+
gem.email = "github@moviepilot.com"
|
50
|
+
gem.authors = ["Alex MacCaw", "Daniel Bornkessel", "Johannes Mentz", "Yan Minagawa", "Benjamin Krause"]
|
51
|
+
# Include your dependencies below. Runtime dependencies are required when using your gem,
|
52
|
+
# and development dependencies are only needed for development (ie running rake tasks, tests, etc)
|
53
|
+
# gem.add_runtime_dependency 'jabber4r', '> 0.1'
|
54
|
+
# gem.add_development_dependency 'rspec', '> 1.2.3'
|
55
|
+
end
|
56
|
+
Jeweler::RubygemsDotOrgTasks.new
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.0
|
data/gwo.gemspec
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = "gwo"
|
3
|
+
s.version = "0.1.4"
|
4
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
5
|
+
s.authors = ["Alex MacCaw", "Daniel Bornkessel", "Johannes Mentz", "Yan Minagawa"]
|
6
|
+
s.date = "2009-09-11"
|
7
|
+
s.email = "daniel@bornkessel.com"
|
8
|
+
s.extra_rdoc_files = %w{ README.markdown }
|
9
|
+
s.files = %w{ lib/gwo.rb MIT-LICENSE README.markdown spec/lib/gwo_spec.rb spec/spec_helper.rb rails/init.rb}
|
10
|
+
s.has_rdoc = true
|
11
|
+
s.rdoc_options = ["--line-numbers", "--inline-source"]
|
12
|
+
s.homepage = "http://github.com/kesselborn/gwo"
|
13
|
+
s.require_paths = %w{ lib }
|
14
|
+
s.rubygems_version = "1.3.1"
|
15
|
+
s.summary = "Plugin to easily make use of Server-Side Dynamic Section Variations with Google Web Optimizer"
|
16
|
+
end
|
data/lib/gwo.rb
ADDED
@@ -0,0 +1,235 @@
|
|
1
|
+
# Author:: Alex MacCaw - alex AT madebymany DOT co DOT uk (original), Daniel Bornkessel - daniel AT bornkessel DOT com
|
2
|
+
# License:: MIT
|
3
|
+
# :include:README.markdown
|
4
|
+
#
|
5
|
+
#
|
6
|
+
|
7
|
+
require 'action_view/helpers/capture_helper'
|
8
|
+
require 'action_view/helpers/tag_helper'
|
9
|
+
module GWO
|
10
|
+
module Helper
|
11
|
+
|
12
|
+
include ::ActionView::Helpers::CaptureHelper
|
13
|
+
include ::ActionView::Helpers::TagHelper
|
14
|
+
|
15
|
+
# start a gwo_experiment
|
16
|
+
#
|
17
|
+
# Params:
|
18
|
+
# * <b>id</b> the id of the experiment (in the google Tracking Script look for something like <tt>pageTracker._trackPageview("/<ID>/test");</tt> )
|
19
|
+
# * <b>uacct</b> account number (in the google Tracking Script look for something like <tt>var pageTracker=_gat._getTracker("<UACCT>");</tt> )
|
20
|
+
# * <b>sections</b> name of the section(s) your page will include; pass in one symbol/string or an array of symbols/strings here
|
21
|
+
# * <b>options</b> hash of possible options:
|
22
|
+
# * <b>:conditions</b> if set to false, the experiment won't be executed -- only the source code of the :original (or 0) variants would be shown. No JavaScript code will be produced. It serves as a kill switch for the gwo experiment. If, for example, you only want to execute an experiment for users that are not logged in, you could pass <tt>:conditions => !logged_in?</tt> here.
|
23
|
+
# * <b>:google_analytics</b> a hash for google analytics tracking: it will call the google analytics script with either the document.location or a virtual url if you provide it. At the end of the url, gwo appends information about which a/b test was executed and which section was the user had in the form of url parameters (s.th. like <tt> http://<url>?ab_test=<id>§ion=<section name > </tt>)
|
24
|
+
# * <b>:account_number</b> the google analytics account id where the tracking should take place
|
25
|
+
# * <b>:virtual_url</b> if you want to choose a different tracking url then the document.location for tracking
|
26
|
+
# * <b>:goal_is_bounce_rate</b> if true, every click on a link on the current view is counted as a goal (NOTE: this includes external links as well)
|
27
|
+
def gwo_experiment(id, uacct, sections = [], options = {}, &block)
|
28
|
+
options = {
|
29
|
+
:conditions => (ENV['RAILS_ENV'] == 'test' ? false : true),
|
30
|
+
:goal_is_bounce_rate => false
|
31
|
+
}.update(options)
|
32
|
+
|
33
|
+
src = gwo_start(id, sections, options)
|
34
|
+
src += capture(&block) if block
|
35
|
+
src += gwo_end(id, uacct, options)
|
36
|
+
|
37
|
+
if options[:goal_is_bounce_rate]
|
38
|
+
src +=<<JS
|
39
|
+
<script type="text/javascript"><!--
|
40
|
+
function addEvent( obj, type, fn ) {
|
41
|
+
if (obj.addEventListener) {
|
42
|
+
obj.addEventListener( type, fn, false );
|
43
|
+
} else if (obj.attachEvent) {
|
44
|
+
obj["e"+type+fn] = fn;
|
45
|
+
obj[type+fn] = function() { obj["e"+type+fn]( window.event ); }
|
46
|
+
obj.attachEvent( "on"+type, obj[type+fn] );
|
47
|
+
}
|
48
|
+
}
|
49
|
+
|
50
|
+
|
51
|
+
addEvent(document, "click", function(event) {
|
52
|
+
if( event.target.nodeName === "A" ) {
|
53
|
+
#{js_logger("'bounce rate goal reached'")}
|
54
|
+
try {
|
55
|
+
var gwoPageTracker=_gat._getTracker("#{uacct}");
|
56
|
+
gwoPageTracker._trackPageview("/#{id}/goal");
|
57
|
+
}catch(err){}
|
58
|
+
}
|
59
|
+
});
|
60
|
+
#{js_logger("'set goal to bounce rate minimization'")}
|
61
|
+
//-->
|
62
|
+
</script>
|
63
|
+
JS
|
64
|
+
end
|
65
|
+
|
66
|
+
src
|
67
|
+
end
|
68
|
+
|
69
|
+
|
70
|
+
# to be included on the conversion page.
|
71
|
+
#
|
72
|
+
# Params:
|
73
|
+
# * <b>id</b> & <b>uacct</b> see gwo_experiment
|
74
|
+
# * <b>options</b>
|
75
|
+
# * :conditions as in gwo_experiment
|
76
|
+
def gwo_conversion(id, uacct, options = {})
|
77
|
+
options = {
|
78
|
+
:conditions => (ENV['RAILS_ENV'] == 'test' ? false : true)
|
79
|
+
}.update(options)
|
80
|
+
|
81
|
+
return js_logger("skipping conversion snippet: a/b variation test switched off", true) if options[:conditions] == false
|
82
|
+
|
83
|
+
%{
|
84
|
+
<script type="text/javascript">
|
85
|
+
#{ js_logger("'conversion for test with id #{id} and uacct #{uacct}'") }
|
86
|
+
if(typeof(_gat)!='object')document.write('<sc'+'ript src="http'+
|
87
|
+
(document.location.protocol=='https:'?'s://ssl':'://www')+
|
88
|
+
'.google-analytics.com/ga.js"></sc'+'ript>')</script>
|
89
|
+
<script type="text/javascript">
|
90
|
+
try {
|
91
|
+
var gwoPageTracker=_gat._getTracker("#{uacct}");
|
92
|
+
gwoPageTracker._trackPageview("/#{id}/goal");
|
93
|
+
}catch(err){}</script>
|
94
|
+
}
|
95
|
+
|
96
|
+
end
|
97
|
+
|
98
|
+
|
99
|
+
# identify a section which is only visible in certain variants
|
100
|
+
#
|
101
|
+
# Params:
|
102
|
+
# * <b>section</b> name of the section
|
103
|
+
# * <b>variation_ids</b> identifiers of the variants in which this content is to be shown. Can be either a name of the variant (== the <b><i>content</i></b> of a variant in the GWO web interface) or a number. The original content has the reserved name <tt>:original</tt> or the number <tt>0</tt> respectivly. If the content should be shown in more than one variant, pass in an array of identifiers. Mixing numbered and named variant ids will result in an exception.
|
104
|
+
# * <b>options</b>
|
105
|
+
# * :conditions as in gwo_experiment
|
106
|
+
def gwo_section(section = "gwo_section", variation_ids = nil, options = {}, &block)
|
107
|
+
options = {
|
108
|
+
:conditions => (ENV['RAILS_ENV'] == 'test' ? false : true)
|
109
|
+
}.update(options)
|
110
|
+
|
111
|
+
variation_ids = [*variation_ids].compact
|
112
|
+
src = ""
|
113
|
+
if is_default_section?(variation_ids)
|
114
|
+
if options[:conditions] == false
|
115
|
+
src += capture(&block)
|
116
|
+
else
|
117
|
+
conditions = (named_variations?(variation_ids) ? variation_ids.map{|x| "GWO_#{section}_name != \"#{x}\""} : variation_ids.map{|x| "GWO_#{section}_number != #{x}"}).join(" && ")
|
118
|
+
|
119
|
+
src += %{ <script>
|
120
|
+
if ( #{ conditions } ) document.write('<no' + 'script>');
|
121
|
+
</script>
|
122
|
+
#{capture(&block) if block_given?}
|
123
|
+
</noscript>
|
124
|
+
}
|
125
|
+
end
|
126
|
+
elsif options[:conditions] == true
|
127
|
+
if !variation_ids.empty?
|
128
|
+
conditions = (named_variations?(variation_ids) ? variation_ids.map{|x| "GWO_#{section}_name == \"#{x}\""} : variation_ids.map{|x| "GWO_#{section}_number == #{x}"}).join(" || ")
|
129
|
+
|
130
|
+
src += %{<script>
|
131
|
+
if ( #{ conditions } ) document.write('</noscript a="');
|
132
|
+
</script><!--">
|
133
|
+
#{capture(&block) if block_given?}
|
134
|
+
<script>document.write('<'+'!'+'-'+'-')</script>-->
|
135
|
+
}
|
136
|
+
end
|
137
|
+
else
|
138
|
+
src = js_logger("skipping snippet for #{variation_ids.join(", ")} variations: a/b variation test switched off", true)
|
139
|
+
end
|
140
|
+
src
|
141
|
+
end
|
142
|
+
|
143
|
+
private
|
144
|
+
def js_logger(text, with_js_tag = false)
|
145
|
+
return "if(typeof(console.log) == 'function') console.log(#{text});" if RAILS_ENV != "test" && RAILS_ENV != "production" && !with_js_tag
|
146
|
+
return "<script type='text/javascript'>if(typeof(console.log) == 'function') console.log(\"#{text}\");</script>" if RAILS_ENV != "test" && RAILS_ENV != "production" && with_js_tag
|
147
|
+
return ""
|
148
|
+
end
|
149
|
+
|
150
|
+
def gwo_start(id, sections = [], options = {})
|
151
|
+
|
152
|
+
return js_logger("skipping start snippet: a/b variation test switched off", true) if options[:conditions] == false
|
153
|
+
|
154
|
+
|
155
|
+
sections = [*sections].compact.empty? ? ["gwo_section"] : [*sections]
|
156
|
+
src = %{
|
157
|
+
<script type='text/javascript'>
|
158
|
+
function utmx_section(){}function utmx(){}
|
159
|
+
(function(){var k='#{id}',d=document,l=d.location,c=d.cookie;function f(n){
|
160
|
+
if(c){var i=c.indexOf(n+'=');if(i>-1){var j=c.indexOf(';',i);return escape(c.substring(i+n.
|
161
|
+
length+1,j<0?c.length:j))}}}var x=f('__utmx'),xx=f('__utmxx'),h=l.hash;
|
162
|
+
d.write('<sc'+'ript src="'+
|
163
|
+
'http'+(l.protocol=='https:'?'s://ssl':'://www')+'.google-analytics.com'
|
164
|
+
+'/siteopt.js?v=1&utmxkey='+k+'&utmx='+(x?x:'')+'&utmxx='+(xx?xx:'')+'&utmxtime='
|
165
|
+
+new Date().valueOf()+(h?'&utmxhash='+escape(h.substr(1)):'')+
|
166
|
+
'" type="text/javascript" charset="utf-8"></sc'+'ript>')})();
|
167
|
+
</script>
|
168
|
+
}
|
169
|
+
|
170
|
+
google_analytics_info = "";
|
171
|
+
section_definitions = "";
|
172
|
+
variable_assignments = "";
|
173
|
+
|
174
|
+
sections.each do |section|
|
175
|
+
section_definitions += "<!-- utmx section name='#{section}' -->\n"
|
176
|
+
|
177
|
+
variable_assignments += %{
|
178
|
+
var GWO_#{section}_name = utmx("variation_content", "#{section}");if( GWO_#{section}_name == undefined) GWO_#{section}_name = 'original';
|
179
|
+
var GWO_#{section}_number = utmx("variation_number", "#{section}");if( GWO_#{section}_number == undefined) GWO_#{section}_number = 0;
|
180
|
+
#{ js_logger("'variant: ' + GWO_#{section}_name") }
|
181
|
+
}
|
182
|
+
google_analytics_info += "google_analytics_info += \"&#{section}=\" + GWO_#{section}_name;" if options[:google_analytics]
|
183
|
+
end
|
184
|
+
|
185
|
+
# GOOGLE TRACKER:
|
186
|
+
if options[:google_analytics]
|
187
|
+
base_url = options[:google_analytics][:virtual_url] ? "\"#{options[:google_analytics][:virtual_url]}\"" : "document.location"
|
188
|
+
variable_assignments += %{
|
189
|
+
window.onload = function(){
|
190
|
+
var google_analytics_info = ''; #{google_analytics_info};
|
191
|
+
try{
|
192
|
+
var gwoGaPageTracker=_gat._getTracker("#{options[:google_analytics][:account_id]}");
|
193
|
+
gwoGaPageTracker._trackPageview(#{base_url} + "?ab_test=#{id}" + google_analytics_info);
|
194
|
+
}catch(err){ #{js_logger("\"error executing google analytics request\"")} }
|
195
|
+
#{js_logger("\"tracking \" + #{base_url} + \"?ab_test=#{id}\" + google_analytics_info + \" to account #{options[:google_analytics][:account_id]}\"")}
|
196
|
+
}
|
197
|
+
}
|
198
|
+
end
|
199
|
+
|
200
|
+
variable_assignments = "<script type='text/javascript'>#{variable_assignments}</script>";
|
201
|
+
|
202
|
+
"#{src}#{section_definitions}#{variable_assignments}"
|
203
|
+
end
|
204
|
+
|
205
|
+
def gwo_end(id, uacct, options)
|
206
|
+
return js_logger("skipping end snippet: a/b variation test switched off", true) if options[:conditions] == false
|
207
|
+
|
208
|
+
%{<script type="text/javascript">
|
209
|
+
if(typeof(_gat)!='object')document.write('<sc'+'ript src="http'+
|
210
|
+
(document.location.protocol=='https:'?'s://ssl':'://www')+
|
211
|
+
'.google-analytics.com/ga.js"></sc'+'ript>')</script>
|
212
|
+
<script type="text/javascript">
|
213
|
+
try {
|
214
|
+
var gwoPageTracker=_gat._getTracker("#{uacct}");
|
215
|
+
gwoPageTracker._trackPageview("/#{id}/test");
|
216
|
+
}catch(err){}</script>
|
217
|
+
}
|
218
|
+
|
219
|
+
end
|
220
|
+
|
221
|
+
def is_default_section?(variation_ids)
|
222
|
+
variation_ids.include?(:original) || variation_ids.include?(0)
|
223
|
+
end
|
224
|
+
|
225
|
+
def named_variations?(variation_ids)
|
226
|
+
raise RuntimeError.new("variation ids can only be either string, symbols or numbers") if [*variation_ids].compact.empty? # catch empty hashes and nil
|
227
|
+
|
228
|
+
return false if [1, *variation_ids].map(&:class).uniq.length == 1 # all variation_ids are FixNums
|
229
|
+
return true if ["string", :symbol, *variation_ids].map(&:class).uniq.length == 2 # all variation_ids are either string or symbol
|
230
|
+
|
231
|
+
raise RuntimeError.new("variation ids can only be either string, symbols or numbers")
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
end
|
data/rails/init.rb
ADDED
@@ -0,0 +1,265 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
require 'lib/gwo'
|
3
|
+
|
4
|
+
describe GWO do
|
5
|
+
include GWO::Helper
|
6
|
+
|
7
|
+
describe "google analytics stuff" do
|
8
|
+
it "should not create any google analytics stuff by default" do
|
9
|
+
gwo_start("gwo_id", "section_name").should_not =~ /google_analytics_info \+= \"§ion_name=\" \+ GWO_section_name_name;/
|
10
|
+
gwo_start("gwo_id", "section_name").should_not =~ /gwoGaPageTracker\(document.location \+ \"\?ab_test=gwo_id\" \+ google_analytics_info\)/
|
11
|
+
end
|
12
|
+
it "should not create google analytics stuff if option is disabled" do
|
13
|
+
gwo_start("gwo_id", "section_name").should_not =~ /google_analytics_info \+= \"§ion_name=\" \+ GWO_section_name_name;/
|
14
|
+
gwo_start("gwo_id", "section_name").should_not =~ /gwoGaPageTracker\(document.location \+ \"\?ab_test=gwo_id\" \+ google_analytics_info\)/
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should create correct google analytics stuff for default urls" do
|
18
|
+
gwo_start("gwo_id", "section_name", :google_analytics => {:account_id => "123456789"}).should =~ /google_analytics_info \+= \"§ion_name=\" \+ GWO_section_name_name;/
|
19
|
+
gwo_start("gwo_id", "section_name", :google_analytics => {:account_id => "123456789"}).should =~ /gwoGaPageTracker._trackPageview\(document.location \+ \"\?ab_test=gwo_id\" \+ google_analytics_info\)/
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should create correct google analytics stuff for static urls" do
|
23
|
+
gwo_start("gwo_id", "section_name", :google_analytics => {:account_id => "123456789", :virtual_url => "http://example.com"}).should =~
|
24
|
+
/google_analytics_info \+= \"§ion_name=\" \+ GWO_section_name_name;/
|
25
|
+
gwo_start("gwo_id", "section_name", :google_analytics => {:account_id => "123456789", :virtual_url => "http://example.com"}).should =~
|
26
|
+
/var gwoGaPageTracker=_gat._getTracker\(\"123456789\"\);/
|
27
|
+
gwo_start("gwo_id", "section_name", :google_analytics => {:account_id => "123456789", :virtual_url => "http://example.com"}).should =~
|
28
|
+
/gwoGaPageTracker._trackPageview\(\"http:\/\/example\.com\" \+ \"\?ab_test=gwo_id\" \+ google_analytics_info\)/
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should create correct google analytics stuff for several sections" do
|
32
|
+
gwo_start("gwo_id", ["section_name1", "section_name2", "section_name3"], :google_analytics => {:account_id => "123456789"}).should =~ /google_analytics_info \+= \"§ion_name1=\" \+ GWO_section_name1_name;/
|
33
|
+
gwo_start("gwo_id", ["section_name1", "section_name2", "section_name3"], :google_analytics => {:account_id => "123456789"}).should =~ /google_analytics_info \+= \"§ion_name2=\" \+ GWO_section_name2_name;/
|
34
|
+
gwo_start("gwo_id", ["section_name1", "section_name2", "section_name3"], :google_analytics => {:account_id => "123456789"}).should =~ /google_analytics_info \+= \"§ion_name3=\" \+ GWO_section_name3_name;/
|
35
|
+
gwo_start("gwo_id", ["section_name1", "section_name2", "section_name3"], :google_analytics => {:account_id => "123456789"}).should =~ /gwoGaPageTracker._trackPageview\(document.location \+ \"\?ab_test=gwo_id\" \+ google_analytics_info\)/
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
describe "named_variations? method" do
|
41
|
+
it "should return false if one numbered variation is passed in" do
|
42
|
+
named_variations?(1).should == false
|
43
|
+
end
|
44
|
+
it "should return false if variations are numbered" do
|
45
|
+
named_variations?([1,2,3]).should == false
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should return true if one string is passed in" do
|
49
|
+
named_variations?("string").should == true
|
50
|
+
end
|
51
|
+
it "should return true if one symbol is passed in" do
|
52
|
+
named_variations?(:symbol).should == true
|
53
|
+
end
|
54
|
+
it "should return true if symbols and strings are mixed" do
|
55
|
+
named_variations?([:symbol, "string", :symbol2]).should == true
|
56
|
+
end
|
57
|
+
it "should throw an exception if numbers and strings are mixed" do
|
58
|
+
lambda {named_variations?([1, :symbol])}.should raise_error(RuntimeError)
|
59
|
+
lambda {named_variations?([1, "string"])}.should raise_error(RuntimeError)
|
60
|
+
lambda {named_variations?([1, "string", :symbol])}.should raise_error(RuntimeError)
|
61
|
+
end
|
62
|
+
it "should throw an exception if one obscure object is passed in" do
|
63
|
+
lambda {named_variations?(Hash.new)}.should raise_error(RuntimeError)
|
64
|
+
lambda {named_variations?(RuntimeError.new)}.should raise_error(RuntimeError)
|
65
|
+
end
|
66
|
+
it "should throw an exception if an array of obscure object is passed in" do
|
67
|
+
lambda {named_variations?([{}, {}])}.should raise_error(RuntimeError)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
describe "gwo_start method" do
|
72
|
+
it "should produce correct output" do
|
73
|
+
gwo_start("gwo_id", "section_name").should =~ /utmx section name='section_name'/
|
74
|
+
gwo_start("gwo_id", "section_name").should =~ /utmx\(\"variation_content\", \"section_name\"\)/
|
75
|
+
gwo_start("gwo_id", "section_name").should =~ /utmx\(\"variation_number\", \"section_name\"\)/
|
76
|
+
gwo_start("gwo_id", "section_name").should =~ /k='gwo_id'/
|
77
|
+
end
|
78
|
+
|
79
|
+
it "should work with just the id parameter set" do
|
80
|
+
gwo_start("gwo_id").should =~ /k='gwo_id'/
|
81
|
+
gwo_start("gwo_id").should =~ /utmx section name='gwo_section'/
|
82
|
+
gwo_start("gwo_id").should =~ /utmx\(\"variation_content\", \"gwo_section\"\)/
|
83
|
+
gwo_start("gwo_id").should =~ /utmx\(\"variation_number\", \"gwo_section\"\)/
|
84
|
+
gwo_start("gwo_id", nil).should =~ /utmx section name='gwo_section'/
|
85
|
+
end
|
86
|
+
|
87
|
+
it "should work with one single section ... section is a symbol" do
|
88
|
+
gwo_start("gwo_id", :section_name).should =~ /utmx section name='section_name'/
|
89
|
+
gwo_start("gwo_id", :section_name).should =~ /utmx\(\"variation_content\", \"section_name\"\)/
|
90
|
+
gwo_start("gwo_id", :section_name).should =~ /utmx\(\"variation_number\", \"section_name\"\)/
|
91
|
+
gwo_start("gwo_id", :section_name).should =~ /k='gwo_id'/
|
92
|
+
end
|
93
|
+
|
94
|
+
it "should work with an array of section" do
|
95
|
+
gwo_start("gwo_id", ["body",:content,"footer"]).should =~ /utmx section name='body'/
|
96
|
+
gwo_start("gwo_id", ["body",:content,"footer"]).should =~ /utmx section name='content'/
|
97
|
+
gwo_start("gwo_id", ["body",:content,"footer"]).should =~ /utmx section name='footer'/
|
98
|
+
|
99
|
+
gwo_start("gwo_id", ["body",:content,"footer"]).should =~ /utmx\(\"variation_content\", \"body\"\)/
|
100
|
+
gwo_start("gwo_id", ["body",:content,"footer"]).should =~ /utmx\(\"variation_content\", \"content\"\)/
|
101
|
+
gwo_start("gwo_id", ["body",:content,"footer"]).should =~ /utmx\(\"variation_content\", \"footer\"\)/
|
102
|
+
|
103
|
+
gwo_start("gwo_id", ["body",:content,"footer"]).should =~ /utmx\(\"variation_number\", \"body\"\)/
|
104
|
+
gwo_start("gwo_id", ["body",:content,"footer"]).should =~ /utmx\(\"variation_number\", \"content\"\)/
|
105
|
+
gwo_start("gwo_id", ["body",:content,"footer"]).should =~ /utmx\(\"variation_number\", \"footer\"\)/
|
106
|
+
end
|
107
|
+
|
108
|
+
it "should return nothing when conditions return false" do
|
109
|
+
gwo_start("id", [], {:conditions => false}).should == ""
|
110
|
+
gwo_start("gwo_id", ["body",:content,"footer"], :conditions => false).should == ""
|
111
|
+
|
112
|
+
gwo_start("gwo_id", ["body",:content,"footer"], {:conditions => false}).should_not =~ /utmx\(\"variation_content\", \"body\"\)/
|
113
|
+
gwo_start("gwo_id", ["body",:content,"footer"], {:conditions => false}).should_not =~ /utmx\(\"variation_content\", \"content\"\)/
|
114
|
+
gwo_start("gwo_id", ["body",:content,"footer"], {:conditions => false}).should_not =~ /utmx\(\"variation_content\", \"footer\"\)/
|
115
|
+
|
116
|
+
gwo_start("gwo_id", ["body",:content,"footer"], :conditions => false).should_not =~ /utmx\(\"variation_number\", \"body\"\)/
|
117
|
+
gwo_start("gwo_id", ["body",:content,"footer"], :conditions => false).should_not =~ /utmx\(\"variation_number\", \"content\"\)/
|
118
|
+
gwo_start("gwo_id", ["body",:content,"footer"], :conditions => false).should_not =~ /utmx\(\"variation_number\", \"footer\"\)/
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
describe "gwo_end method" do
|
123
|
+
it "should produce correct output" do
|
124
|
+
gwo_end("gwo_id", "gwo_uacct", :conditions => true).should =~ /getTracker\(\"gwo_uacct\"\)/
|
125
|
+
gwo_end("gwo_id", "gwo_uacct", :conditions => true).should =~ /trackPageview\(\"\/gwo_id\/test\"\)/
|
126
|
+
end
|
127
|
+
|
128
|
+
it "should return nothing if conditions are not met" do
|
129
|
+
gwo_end("gwo_id", "gwo_uacct", {:conditions => false}).should_not =~ /getTracker\(\"gwo_uacct\"\)/
|
130
|
+
gwo_end("gwo_id", "gwo_uacct", :conditions => false).should == ""
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
describe "gw_conversion method" do
|
135
|
+
it "should produce correct output" do
|
136
|
+
gwo_conversion("gwo_id", "gwo_uacct").should =~ /getTracker\(\"gwo_uacct\"\)/
|
137
|
+
gwo_conversion("gwo_id", "gwo_uacct").should =~ /trackPageview\(\"\/gwo_id\/goal\"\)/
|
138
|
+
end
|
139
|
+
|
140
|
+
it "should return nothing when conditions are not met" do
|
141
|
+
gwo_conversion("gwo_id", "gwo_uacct", {:conditions => false}).should == ""
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
describe "goal is bounce rate" do
|
146
|
+
before(:each) do
|
147
|
+
stub!(:output_buffer=).and_return "foo"
|
148
|
+
stub!(:output_buffer).and_return "foo"
|
149
|
+
end
|
150
|
+
|
151
|
+
it "should create link bindings when goal is bounce rate" do
|
152
|
+
gwo_experiment("id", "uacct", sections = [:foo, :bar], :goal_is_bounce_rate => true).should =~ /addEventListener/
|
153
|
+
end
|
154
|
+
|
155
|
+
it "should not create link bindings when goal is not bounce rate" do
|
156
|
+
gwo_experiment("id", "uacct", sections = [:foo, :bar]).should_not =~ /addEventListener/
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
describe "gwo_section method with named sections" do
|
161
|
+
before(:each) do
|
162
|
+
stub!(:output_buffer=).and_return "foo"
|
163
|
+
stub!(:output_buffer).and_return "foo"
|
164
|
+
end
|
165
|
+
|
166
|
+
it "should return nothing when conditions are not met and the variation is not the original" do
|
167
|
+
gwo_section("gwo_section", ["foo","bar"], {:conditions => false}).should == ""
|
168
|
+
end
|
169
|
+
|
170
|
+
it "should return original output without javascript if conditions are not met and original is the variation " do
|
171
|
+
gwo_section("gwo_section", :original, {:conditions => false}) { "this is the content" }.should == "this is the content"
|
172
|
+
end
|
173
|
+
|
174
|
+
it "should return original output with javascript if ignore is unset and original is the variation " do
|
175
|
+
gwo_section("gwo_section", :original) { "this is the content" }.should =~ /this is the content/
|
176
|
+
gwo_section("gwo_section", :original) { "this is the content" }.should =~ /( GWO_gwo_section_name != \"original\" )/
|
177
|
+
end
|
178
|
+
|
179
|
+
it "should only write one javascript block if the section is used for original and variations" do
|
180
|
+
gwo_section("section", [:original, :variation1, :variation2]) { "this is the content" }.should =~ /this is the content/
|
181
|
+
gwo_section("section", [:original, :variation1, :variation2]) { "this is the content" }.should =~ /( GWO_section_name != \"original\" && GWO_section_name != \"variation1\" && GWO_section_name != \"variation2\" )/
|
182
|
+
end
|
183
|
+
|
184
|
+
it "should write block for one variant" do
|
185
|
+
gwo_section("section",:testing) { "this is the content" }.should =~ /this is the content/
|
186
|
+
gwo_section("section",:testing) { "this is the content" }.should =~ /( GWO_section_name == \"testing\" )/
|
187
|
+
end
|
188
|
+
|
189
|
+
it "should write one block but enabled for all given variants " do
|
190
|
+
gwo_section("section",[:testing, :still_testing]) { "this is the content" }.should =~ /this is the content/
|
191
|
+
gwo_section("section",[:testing, :still_testing]) { "this is the content" }.should =~ /( GWO_section_name == \"testing\" || GWO_section_name == \"still_testing\" )/
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
describe "gwo_section method with numbered sections" do
|
196
|
+
before(:each) do
|
197
|
+
stub!(:output_buffer=).and_return "foo"
|
198
|
+
stub!(:output_buffer).and_return "foo"
|
199
|
+
end
|
200
|
+
|
201
|
+
|
202
|
+
it "should return nothing when conditions are not met and the variation is not the original" do
|
203
|
+
gwo_section("gwo_section", [1, 2], {:conditions => false}).should == ""
|
204
|
+
end
|
205
|
+
|
206
|
+
it "should return original output without javascript if conditions are not met and original is the variation " do
|
207
|
+
gwo_section("gwo_section", 0, {:conditions => false}) { "this is the content" }.should == "this is the content"
|
208
|
+
end
|
209
|
+
|
210
|
+
it "should return original output with javascript if ignore is unset and original is the variation " do
|
211
|
+
gwo_section("gwo_section", 0) { "this is the content" }.should =~ /this is the content/
|
212
|
+
gwo_section("gwo_section", 0) { "this is the content" }.should =~ /( GWO_gwo_section_number != 0 )/
|
213
|
+
end
|
214
|
+
|
215
|
+
it "should only write one javascript block if the section is used for original and variations" do
|
216
|
+
gwo_section("section", [0, 1, 2]) { "this is the content" }.should =~ /this is the content/
|
217
|
+
gwo_section("section", [0, 1, 2]) { "this is the content" }.should =~ /( GWO_section_number != 0 && GWO_section_number != 1 && GWO_section_number != 2 )/
|
218
|
+
end
|
219
|
+
|
220
|
+
it "should write block for one variant" do
|
221
|
+
gwo_section("section",1) { "this is the content" }.should =~ /this is the content/
|
222
|
+
gwo_section("section",1) { "this is the content" }.should =~ /( GWO_section_number == 1 )/
|
223
|
+
end
|
224
|
+
|
225
|
+
it "should write one block but enabled for all given variants " do
|
226
|
+
gwo_section("section",[1, 2]) { "this is the content" }.should =~ /this is the content/
|
227
|
+
gwo_section("section",[1, 2]) { "this is the content" }.should =~ /( GWO_section_number == 1 || GWO_section_number == 2 )/
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
describe "gwo_methods in rails testing environment" do
|
232
|
+
before(:all) do
|
233
|
+
@stored_rails_env = ENV['RAILS_ENV']
|
234
|
+
ENV['RAILS_ENV'] = 'test'
|
235
|
+
end
|
236
|
+
|
237
|
+
after(:all) do
|
238
|
+
@stored_rails_env.nil? ? ENV.delete('RAILS_ENV') : ENV['RAILS_ENV'] = @stored_rails_env
|
239
|
+
end
|
240
|
+
|
241
|
+
before(:each) do
|
242
|
+
stub!(:output_buffer=).and_return "foo"
|
243
|
+
stub!(:output_buffer).and_return "foo"
|
244
|
+
end
|
245
|
+
|
246
|
+
it "should not render javascript (:gwo_experiment)" do
|
247
|
+
result = gwo_experiment("id", "uacct", sections = [:foo, :bar])
|
248
|
+
result.should == ''
|
249
|
+
result.should_not =~ /<script type="text\/javascript">/
|
250
|
+
end
|
251
|
+
|
252
|
+
it "should not render javascript (:gwo_experiment)" do
|
253
|
+
result = gwo_conversion("gwo_id", "gwo_uacct")
|
254
|
+
result.should == ''
|
255
|
+
result.should_not =~ /<script type="text\/javascript">/
|
256
|
+
end
|
257
|
+
|
258
|
+
it "should not render javascript (:gwo_section)" do
|
259
|
+
result = gwo_section("gwo_section", [1, 2])
|
260
|
+
result.should == ''
|
261
|
+
result.should_not =~ /<script>/
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
265
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,141 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: mp-gwo
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 27
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
- 0
|
10
|
+
version: 0.1.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Alex MacCaw
|
14
|
+
- Daniel Bornkessel
|
15
|
+
- Johannes Mentz
|
16
|
+
- Yan Minagawa
|
17
|
+
- Benjamin Krause
|
18
|
+
autorequire:
|
19
|
+
bindir: bin
|
20
|
+
cert_chain: []
|
21
|
+
|
22
|
+
date: 2011-03-28 00:00:00 +02:00
|
23
|
+
default_executable:
|
24
|
+
dependencies:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
type: :runtime
|
27
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ~>
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
hash: 9
|
33
|
+
segments:
|
34
|
+
- 1
|
35
|
+
- 3
|
36
|
+
version: "1.3"
|
37
|
+
name: rspec
|
38
|
+
version_requirements: *id001
|
39
|
+
prerelease: false
|
40
|
+
- !ruby/object:Gem::Dependency
|
41
|
+
type: :runtime
|
42
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
43
|
+
none: false
|
44
|
+
requirements:
|
45
|
+
- - ~>
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
hash: 9
|
48
|
+
segments:
|
49
|
+
- 1
|
50
|
+
- 3
|
51
|
+
version: "1.3"
|
52
|
+
name: rspec-rails
|
53
|
+
version_requirements: *id002
|
54
|
+
prerelease: false
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
type: :runtime
|
57
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
58
|
+
none: false
|
59
|
+
requirements:
|
60
|
+
- - "="
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
hash: 21
|
63
|
+
segments:
|
64
|
+
- 2
|
65
|
+
- 3
|
66
|
+
- 11
|
67
|
+
version: 2.3.11
|
68
|
+
name: rails
|
69
|
+
version_requirements: *id003
|
70
|
+
prerelease: false
|
71
|
+
- !ruby/object:Gem::Dependency
|
72
|
+
type: :runtime
|
73
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
74
|
+
none: false
|
75
|
+
requirements:
|
76
|
+
- - ~>
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
hash: 7
|
79
|
+
segments:
|
80
|
+
- 1
|
81
|
+
- 5
|
82
|
+
- 2
|
83
|
+
version: 1.5.2
|
84
|
+
name: jeweler
|
85
|
+
version_requirements: *id004
|
86
|
+
prerelease: false
|
87
|
+
description: Google Website Optimizer Helper
|
88
|
+
email: github@moviepilot.com
|
89
|
+
executables: []
|
90
|
+
|
91
|
+
extensions: []
|
92
|
+
|
93
|
+
extra_rdoc_files:
|
94
|
+
- README.markdown
|
95
|
+
files:
|
96
|
+
- MIT-LICENSE
|
97
|
+
- README.markdown
|
98
|
+
- Rakefile
|
99
|
+
- VERSION
|
100
|
+
- gwo.gemspec
|
101
|
+
- lib/gwo.rb
|
102
|
+
- rails/init.rb
|
103
|
+
- spec/lib/gwo_spec.rb
|
104
|
+
- spec/spec_helper.rb
|
105
|
+
has_rdoc: true
|
106
|
+
homepage: http://github.com/moviepilot/gwo
|
107
|
+
licenses:
|
108
|
+
- MIT
|
109
|
+
post_install_message:
|
110
|
+
rdoc_options: []
|
111
|
+
|
112
|
+
require_paths:
|
113
|
+
- lib
|
114
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
115
|
+
none: false
|
116
|
+
requirements:
|
117
|
+
- - ">="
|
118
|
+
- !ruby/object:Gem::Version
|
119
|
+
hash: 3
|
120
|
+
segments:
|
121
|
+
- 0
|
122
|
+
version: "0"
|
123
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
124
|
+
none: false
|
125
|
+
requirements:
|
126
|
+
- - ">="
|
127
|
+
- !ruby/object:Gem::Version
|
128
|
+
hash: 3
|
129
|
+
segments:
|
130
|
+
- 0
|
131
|
+
version: "0"
|
132
|
+
requirements: []
|
133
|
+
|
134
|
+
rubyforge_project:
|
135
|
+
rubygems_version: 1.5.0
|
136
|
+
signing_key:
|
137
|
+
specification_version: 3
|
138
|
+
summary: Google Website Optimizer Helper
|
139
|
+
test_files:
|
140
|
+
- spec/lib/gwo_spec.rb
|
141
|
+
- spec/spec_helper.rb
|