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 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://&lt;url&gt;?ab_test=&lt;id&gt;&section=&lt;section name &gt; </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,2 @@
1
+ # Include hook code here
2
+ ActionView::Base.send(:include, GWO::Helper)
@@ -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 \+= \"&section_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 \+= \"&section_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 \+= \"&section_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 \+= \"&section_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 \+= \"&section_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 \+= \"&section_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 \+= \"&section_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
@@ -0,0 +1,4 @@
1
+ # This file is copied to ~/spec when you run 'ruby script/generate rspec'
2
+ # from the project root directory.
3
+ require 'spec'
4
+ RAILS_ENV="test"
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