File Coverage

blib/lib/Dancer2/Plugin/Sixpack.pm
Criterion Covered Total %
statement 48 48 100.0
branch 11 16 68.7
condition 7 10 70.0
subroutine 8 8 100.0
pod 1 1 100.0
total 75 83 90.3


line stmt bran cond sub pod time code
1             package Dancer2::Plugin::Sixpack;
2              
3 2     2   604891 use 5.006;
  2         9  
4 2     2   12 use strict;
  2         4  
  2         60  
5 2     2   11 use warnings FATAL => 'all';
  2         12  
  2         107  
6              
7 2     2   1898 use Dancer2::Plugin;
  2         166776  
  2         15  
8 2     2   4772 use WWW::Sixpack;
  2         155472  
  2         1476  
9              
10             our $VERSION = '0.03';
11              
12             my $conf;
13              
14             =head1 NAME
15              
16             Dancer2::Plugin::Sixpack - Dancer2's plugin for WWW::Sixpack
17              
18             =head1 DESCRIPTION
19              
20             This plugin gives you the ability to do A/B testing within Dancer2 easily,
21             using L.
22              
23             It handles the client_id transparantly through Dancer2's Session plugin.
24              
25             =head1 SYNOPSIS
26              
27             use Dancer2;
28             use Dancer2::Plugin::Sixpack;
29              
30             get '/route' => sub {
31             my $variant = experiment 'decimal_dot_comma', [ 'comma', 'dot' ];
32              
33             $price =~ s/\.(?=[0-9]{2})$/,/
34             if( $variant eq 'comma' );
35             # ...
36             };
37              
38             get '/some_click' => sub {
39             convert 'decimal_dot_comma', 'click';
40             redirect $somewhere;
41             };
42              
43             get '/confirmation' => sub {
44             convert 'decimal_dot_comma';
45             # ...
46             };
47              
48              
49             =head1 CONFIGURATION
50              
51             There are no mandatory settings.
52              
53             plugins:
54             Sixpack:
55             host: http://localhost:5000
56             experiments:
57             decimal_dot_comma:
58             - comma
59             - dot
60             beer:
61             - duvel
62             - budweiser
63              
64             The experiments can be generated on the fly without defining them. See below
65             for more information.
66              
67             =head1 KEYWORDS
68              
69             =head2 experiment
70              
71             Fetch the alternative used for the experiment name passed in.
72              
73             The experiment and its' alternatives may be defined in the configuration. If
74             they're not defined, the experiment will be created (if you provided the
75             alternatives arrayref).
76              
77             Examples:
78              
79             # experiment defined in config:
80             my $variant = exeriment 'known-experiment';
81              
82             # experiment not defined
83             my $variant = experiment 'on-the-fly', [ 'alt-1', 'alt-2' ];
84              
85             The client_id will be fetched from session, or generated if needed.
86              
87             The client's IP address and user agent string are automatically
88             added to the request for bot detection.
89              
90             Alternatives can be forced by params like "sixpack-force-$experiment=$alt"
91              
92             Returns the alternative name chosen.
93              
94             =cut
95              
96             register experiment => sub {
97 2     2   199157 my ($dsl, $name, $alternatives) = @_;
98              
99 2         14 my $sixpack = $dsl->get_sixpack();
100              
101             # stored alternatives?
102 2 100 66     5565 if( !$alternatives && defined $conf->{experiments}{$name} ) {
103 1         3 $alternatives = $conf->{experiments}{$name};
104             }
105              
106             # user info
107 2         6 my %options = ();
108 2 50       33 $options{ip_address} = $dsl->app->request->address
109             if $dsl->app->request->address;
110 2 50       49 $options{user_agent} = $dsl->app->request->agent
111             if $dsl->app->request->agent;
112              
113             # force if requested
114 2 50       64 $options{force} = $dsl->app->request->param("sixpack-force-$name")
115             if $dsl->app->request->param("sixpack-force-$name");
116              
117 2         37 my $alt = $sixpack
118             ->participate( $name, $alternatives, \%options );
119              
120 2   100     2903 my $experiments = $dsl->app->session->read('sixpack_experiments') || { };
121 2         86 $experiments->{$name} = $alt->{alternative}{name};
122              
123 2         46 $dsl->app->session->write('sixpack_id', $alt->{client_id});
124 2         845 $dsl->app->session->write('sixpack_experiments', $experiments);
125              
126 2         210 return $alt->{alternative}{name};
127             };
128              
129             =head2 convert
130              
131             Convert a user.
132              
133             Provide the experiment and (optional) a KPI to track conversion on.
134             If the KPI doesn't exist yet, it will be created.
135              
136             When no experiment name is given, we try to fetch the experiments
137             from the user's session and convert on all of the found experiments.
138              
139             Returns a hashref with { 'experiment' => 'status' }
140              
141             =cut
142              
143             register convert => sub {
144 2     2   22658 my ($dsl, $experiment, $kpi) = @_;
145              
146 2         5 my %return;
147 2         7 my $sixpack = $dsl->get_sixpack();
148              
149 2 100       1331 if( $experiment ) {
150             # specific experiment given
151 1         7 my $res = $sixpack->convert($experiment, $kpi);
152 1         1253 $return{$experiment} = $res->{status};
153             } else {
154             # no experiments given, look them up
155 1   50     33 my $experiments = $dsl->app->session->read('sixpack_experiments') || { };
156 1         38 for my $exp (keys %{$experiments}) {
  1         5  
157 2         10 my $res = $sixpack->convert($exp);
158 2         2045 $return{$exp} = $res->{status};
159             }
160             }
161              
162 2         16 return \%return;
163             };
164              
165             =head2 get_sixpack
166              
167             Internal method to construct the L object.
168              
169             =cut
170              
171             sub get_sixpack {
172 4     4 1 10 my $dsl = shift;
173              
174 4   66     23 $conf ||= plugin_setting();
175              
176 4         125 my %options;
177 4         75 my $client_id = $dsl->app->session->read('sixpack_id');
178              
179             # need to pass info on to the sixpack object?
180 4 50       13631 $options{host} = $conf->{host} if( defined $conf->{host} );
181 4 100       22 $options{client_id} = $client_id if( defined $client_id );
182 4 50       18 $options{ua} = $conf->{ua} if( defined $conf->{ua} );
183              
184 4         43 return WWW::Sixpack->new(%options);
185             }
186              
187             =head1 AUTHOR
188              
189             Menno Blom, C<< >>
190              
191             =head1 BUGS
192              
193             Please report any bugs or feature requests to C, or through
194             the web interface at L. I will be notified, and then you'll
195             automatically be notified of progress on your bug as I make changes.
196              
197             =head1 COPYRIGHT
198              
199             Copyright 2014- Menno Blom
200              
201             =head1 LICENSE
202              
203             This library is free software; you can redistribute it and/or modify
204             it under the same terms as Perl itself.
205              
206             =cut
207              
208             register_plugin for_versions => [ 2 ] ;
209              
210             1; # End of Dancer2::Plugin::Sixpack