| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package Wishlist; | 
| 2 | 2 |  |  | 2 |  | 1124 | use Mojo::Base 'Mojolicious'; | 
|  | 2 |  |  |  |  | 3 |  | 
|  | 2 |  |  |  |  | 14 |  | 
| 3 |  |  |  |  |  |  |  | 
| 4 | 2 |  |  | 2 |  | 81434 | use File::Share; | 
|  | 2 |  |  |  |  | 11056 |  | 
|  | 2 |  |  |  |  | 74 |  | 
| 5 | 2 |  |  | 2 |  | 11 | use Mojo::File; | 
|  | 2 |  |  |  |  | 3 |  | 
|  | 2 |  |  |  |  | 64 |  | 
| 6 | 2 |  |  | 2 |  | 9 | use Mojo::Home; | 
|  | 2 |  |  |  |  | 5 |  | 
|  | 2 |  |  |  |  | 46 |  | 
| 7 | 2 |  |  | 2 |  | 813 | use Mojo::SQLite; | 
|  | 2 |  |  |  |  | 104277 |  | 
|  | 2 |  |  |  |  | 20 |  | 
| 8 | 2 |  |  | 2 |  | 853 | use LinkEmbedder; | 
|  | 2 |  |  |  |  | 7753 |  | 
|  | 2 |  |  |  |  | 12 |  | 
| 9 | 2 |  |  | 2 |  | 792 | use Wishlist::Model; | 
|  | 2 |  |  |  |  | 7 |  | 
|  | 2 |  |  |  |  | 20 |  | 
| 10 |  |  |  |  |  |  |  | 
| 11 |  |  |  |  |  |  | our $VERSION = '0.01'; | 
| 12 |  |  |  |  |  |  | $VERSION = eval $VERSION; | 
| 13 |  |  |  |  |  |  |  | 
| 14 |  |  |  |  |  |  | has dist_dir => sub { | 
| 15 |  |  |  |  |  |  | return Mojo::File->new( | 
| 16 |  |  |  |  |  |  | File::Share::dist_dir('Wishlist') | 
| 17 |  |  |  |  |  |  | ); | 
| 18 |  |  |  |  |  |  | }; | 
| 19 |  |  |  |  |  |  |  | 
| 20 |  |  |  |  |  |  | has home => sub { | 
| 21 |  |  |  |  |  |  | my $home = $ENV{WISHLIST_HOME}; | 
| 22 |  |  |  |  |  |  | return Mojo::Home->new($home ? $home : ())->to_abs; | 
| 23 |  |  |  |  |  |  | }; | 
| 24 |  |  |  |  |  |  |  | 
| 25 |  |  |  |  |  |  | has site_name => sub { | 
| 26 |  |  |  |  |  |  | my $app = shift; | 
| 27 |  |  |  |  |  |  | return $app->config->{site_name} || 'Mojo Wishlist'; | 
| 28 |  |  |  |  |  |  | }; | 
| 29 |  |  |  |  |  |  |  | 
| 30 |  |  |  |  |  |  | has sqlite => sub { | 
| 31 |  |  |  |  |  |  | my $app = shift; | 
| 32 |  |  |  |  |  |  |  | 
| 33 |  |  |  |  |  |  | # determine the storage location | 
| 34 |  |  |  |  |  |  | my $file = $app->config->{database} || 'wishlist.db'; | 
| 35 |  |  |  |  |  |  | unless ($file =~ /^:/) { | 
| 36 |  |  |  |  |  |  | $file = Mojo::File->new($file); | 
| 37 |  |  |  |  |  |  | unless ($file->is_abs) { | 
| 38 |  |  |  |  |  |  | $file = $app->home->child("$file"); | 
| 39 |  |  |  |  |  |  | } | 
| 40 |  |  |  |  |  |  | } | 
| 41 |  |  |  |  |  |  |  | 
| 42 |  |  |  |  |  |  | my $sqlite = Mojo::SQLite->new | 
| 43 |  |  |  |  |  |  | ->from_filename("$file") | 
| 44 |  |  |  |  |  |  | ->auto_migrate(1); | 
| 45 |  |  |  |  |  |  |  | 
| 46 |  |  |  |  |  |  | # attach migrations file | 
| 47 |  |  |  |  |  |  | $sqlite->migrations->from_file( | 
| 48 |  |  |  |  |  |  | $app->dist_dir->child('wishlist.sql') | 
| 49 |  |  |  |  |  |  | )->name('wishlist'); | 
| 50 |  |  |  |  |  |  |  | 
| 51 |  |  |  |  |  |  | return $sqlite; | 
| 52 |  |  |  |  |  |  | }; | 
| 53 |  |  |  |  |  |  |  | 
| 54 |  |  |  |  |  |  | sub startup { | 
| 55 | 2 |  |  | 2 | 1 | 21393 | my $app = shift; | 
| 56 |  |  |  |  |  |  |  | 
| 57 |  |  |  |  |  |  | $app->plugin('Config' => { | 
| 58 |  |  |  |  |  |  | file => $ENV{WISHLIST_CONFIG}, | 
| 59 | 2 |  |  |  |  | 12 | default => {}, | 
| 60 |  |  |  |  |  |  | }); | 
| 61 |  |  |  |  |  |  |  | 
| 62 | 2 | 50 |  |  |  | 2389 | if (my $secrets = $app->config->{secrets}) { | 
| 63 | 0 |  |  |  |  | 0 | $app->secrets($secrets); | 
| 64 |  |  |  |  |  |  | } | 
| 65 |  |  |  |  |  |  |  | 
| 66 |  |  |  |  |  |  | $app->renderer->paths([ | 
| 67 | 2 |  |  |  |  | 18 | $app->dist_dir->child('templates'), | 
| 68 |  |  |  |  |  |  | ]); | 
| 69 |  |  |  |  |  |  |  | 
| 70 |  |  |  |  |  |  | $app->helper(link => sub { | 
| 71 | 0 |  |  | 0 |  | 0 | my $c = shift; | 
| 72 | 0 |  |  |  |  | 0 | state $le = LinkEmbedder->new; | 
| 73 | 0 |  |  |  |  | 0 | return $le->get(@_); | 
| 74 | 2 |  |  |  |  | 471 | }); | 
| 75 |  |  |  |  |  |  |  | 
| 76 |  |  |  |  |  |  | $app->helper(model => sub { | 
| 77 | 1 |  |  | 1 |  | 457 | my $c = shift; | 
| 78 | 1 |  |  |  |  | 5 | return Wishlist::Model->new( | 
| 79 |  |  |  |  |  |  | sqlite => $c->app->sqlite, | 
| 80 |  |  |  |  |  |  | ); | 
| 81 | 2 |  |  |  |  | 46 | }); | 
| 82 |  |  |  |  |  |  |  | 
| 83 |  |  |  |  |  |  | $app->helper(user => sub { | 
| 84 | 0 |  |  | 0 |  | 0 | my ($c, $username) = @_; | 
| 85 | 0 |  | 0 |  |  | 0 | $username ||= $c->stash->{username} || $c->session->{username}; | 
|  |  |  | 0 |  |  |  |  | 
| 86 | 0 | 0 |  |  |  | 0 | return {} unless $username; | 
| 87 | 0 |  | 0 |  |  | 0 | return $c->model->user($username) || {}; | 
| 88 | 2 |  |  |  |  | 25 | }); | 
| 89 |  |  |  |  |  |  |  | 
| 90 |  |  |  |  |  |  | $app->helper(users => sub { | 
| 91 | 0 |  |  | 0 |  | 0 | my $c = shift; | 
| 92 | 0 |  |  |  |  | 0 | return $c->model->all_users; | 
| 93 | 2 |  |  |  |  | 34 | }); | 
| 94 |  |  |  |  |  |  |  | 
| 95 | 2 |  |  |  |  | 23 | my $r = $app->routes; | 
| 96 |  |  |  |  |  |  | $r->get('/' => sub { | 
| 97 | 0 |  |  | 0 |  | 0 | my $c = shift; | 
| 98 | 0 | 0 |  |  |  | 0 | my $template = $c->session->{username} ? 'list' : 'login'; | 
| 99 | 0 |  |  |  |  | 0 | $c->render($template); | 
| 100 | 2 |  |  |  |  | 24 | }); | 
| 101 |  |  |  |  |  |  |  | 
| 102 | 2 |  |  |  |  | 518 | $r->get('/list/:username')->to(template => 'list')->name('list'); | 
| 103 |  |  |  |  |  |  |  | 
| 104 | 2 |  |  |  |  | 552 | $r->get('/add')->to('List#show_add')->name('show_add'); | 
| 105 | 2 |  |  |  |  | 499 | $r->post('/add')->to('List#do_add')->name('do_add'); | 
| 106 |  |  |  |  |  |  |  | 
| 107 | 2 |  |  |  |  | 395 | $r->post('/update')->to('List#update')->name('update'); | 
| 108 | 2 |  |  |  |  | 422 | $r->post('/remove')->to('List#remove')->name('remove'); | 
| 109 |  |  |  |  |  |  |  | 
| 110 | 2 |  |  |  |  | 401 | $r->get('/register')->to(template => 'register')->name('show_register'); | 
| 111 | 2 |  |  |  |  | 402 | $r->post('/register')->to('Access#register')->name('do_register'); | 
| 112 | 2 |  |  |  |  | 428 | $r->post('/login')->to('Access#login')->name('login'); | 
| 113 | 2 |  |  |  |  | 420 | $r->any('/logout')->to('Access#logout')->name('logout'); | 
| 114 |  |  |  |  |  |  |  | 
| 115 |  |  |  |  |  |  | } | 
| 116 |  |  |  |  |  |  |  | 
| 117 |  |  |  |  |  |  | 1; | 
| 118 |  |  |  |  |  |  |  | 
| 119 |  |  |  |  |  |  | =head1 NAME | 
| 120 |  |  |  |  |  |  |  | 
| 121 |  |  |  |  |  |  | Wishlist - A multi-user web application for tracking wanted items. | 
| 122 |  |  |  |  |  |  |  | 
| 123 |  |  |  |  |  |  | =head1 SYNOPSIS | 
| 124 |  |  |  |  |  |  |  | 
| 125 |  |  |  |  |  |  | $ wishlist daemon | 
| 126 |  |  |  |  |  |  | $ wishlist prefork | 
| 127 |  |  |  |  |  |  | $ hypnotoad `which wishlist` | 
| 128 |  |  |  |  |  |  |  | 
| 129 |  |  |  |  |  |  | =head1 DESCRIPTION | 
| 130 |  |  |  |  |  |  |  | 
| 131 |  |  |  |  |  |  | L is a L application for storing and sharing wishlists derived from external urls. | 
| 132 |  |  |  |  |  |  | Users paste urls and the app fetches data from those sites. | 
| 133 |  |  |  |  |  |  | Users can then add the item to their wishlist. | 
| 134 |  |  |  |  |  |  | Other users can then mark the item as purchased. | 
| 135 |  |  |  |  |  |  |  | 
| 136 |  |  |  |  |  |  | The application is very raw, lots of feature improvement is possible. | 
| 137 |  |  |  |  |  |  | It was developed as examples from L during the L<2017 Mojolicious Advent Calendar|https://mojolicious.io/blog/tag/advent/>. | 
| 138 |  |  |  |  |  |  | I hope that it will continue to improve, with community collaboration, into a fully fledge competitor to cloud solutions that track and mine your data. | 
| 139 |  |  |  |  |  |  |  | 
| 140 |  |  |  |  |  |  | =head1 DEPLOYING | 
| 141 |  |  |  |  |  |  |  | 
| 142 |  |  |  |  |  |  | As L is just a L application, all of the L options are available for deployment. | 
| 143 |  |  |  |  |  |  |  | 
| 144 |  |  |  |  |  |  | =head2 APPLICATION HOME | 
| 145 |  |  |  |  |  |  |  | 
| 146 |  |  |  |  |  |  | The application home is where L stores data and looks for configuration. | 
| 147 |  |  |  |  |  |  | It can be set by setting C in your environment, otherwise your current working directory is used. | 
| 148 |  |  |  |  |  |  |  | 
| 149 |  |  |  |  |  |  | =head2 CONFIGURATION | 
| 150 |  |  |  |  |  |  |  | 
| 151 |  |  |  |  |  |  | { | 
| 152 |  |  |  |  |  |  | site_name => 'Family Wishlist', | 
| 153 |  |  |  |  |  |  | secrets => ['a very secret string'], | 
| 154 |  |  |  |  |  |  | database => '/path/to/database/file.db', | 
| 155 |  |  |  |  |  |  | } | 
| 156 |  |  |  |  |  |  |  | 
| 157 |  |  |  |  |  |  | A configuration file is highly recommended. | 
| 158 |  |  |  |  |  |  | Its contents should evaluate to a Perl data structure. | 
| 159 |  |  |  |  |  |  | The easiest usage is to create a configuration file in the L"APPLICATION HOME">. | 
| 160 |  |  |  |  |  |  | The file should be called C or C if per-mode configuration is desired. | 
| 161 |  |  |  |  |  |  | Alternatively, an absolute path to the configuration file can be given via C. | 
| 162 |  |  |  |  |  |  |  | 
| 163 |  |  |  |  |  |  | The allowed configuration options are | 
| 164 |  |  |  |  |  |  |  | 
| 165 |  |  |  |  |  |  | =over | 
| 166 |  |  |  |  |  |  |  | 
| 167 |  |  |  |  |  |  | =item site_name | 
| 168 |  |  |  |  |  |  |  | 
| 169 |  |  |  |  |  |  | A string specifying the name of the site. | 
| 170 |  |  |  |  |  |  | Used in the link to the application root. | 
| 171 |  |  |  |  |  |  | Defaults to C. | 
| 172 |  |  |  |  |  |  |  | 
| 173 |  |  |  |  |  |  | =item secrets | 
| 174 |  |  |  |  |  |  |  | 
| 175 |  |  |  |  |  |  | An array reference of strings used to sign storage cookies. | 
| 176 |  |  |  |  |  |  | While this value is optional, it is highly recommended. | 
| 177 |  |  |  |  |  |  | Learn about how these work at L. | 
| 178 |  |  |  |  |  |  |  | 
| 179 |  |  |  |  |  |  | =item database | 
| 180 |  |  |  |  |  |  |  | 
| 181 |  |  |  |  |  |  | Path to the file used to store data via L. | 
| 182 |  |  |  |  |  |  | If not specified the default value will be C. | 
| 183 |  |  |  |  |  |  | Any relative values will be relative to the L"APPLICATION HOME">. | 
| 184 |  |  |  |  |  |  |  | 
| 185 |  |  |  |  |  |  | =item hypnotoad | 
| 186 |  |  |  |  |  |  |  | 
| 187 |  |  |  |  |  |  | Hypnotoad uses the application's configuration for deployment parameters. | 
| 188 |  |  |  |  |  |  | If you deploy using it, you probably should read L. | 
| 189 |  |  |  |  |  |  |  | 
| 190 |  |  |  |  |  |  | =back | 
| 191 |  |  |  |  |  |  |  | 
| 192 |  |  |  |  |  |  | =head1 SEE ALSO | 
| 193 |  |  |  |  |  |  |  | 
| 194 |  |  |  |  |  |  | =over | 
| 195 |  |  |  |  |  |  |  | 
| 196 |  |  |  |  |  |  | =item L | 
| 197 |  |  |  |  |  |  |  | 
| 198 |  |  |  |  |  |  | =item L | 
| 199 |  |  |  |  |  |  |  | 
| 200 |  |  |  |  |  |  | =item L | 
| 201 |  |  |  |  |  |  |  | 
| 202 |  |  |  |  |  |  | =back | 
| 203 |  |  |  |  |  |  |  | 
| 204 |  |  |  |  |  |  | =head1 SOURCE REPOSITORY | 
| 205 |  |  |  |  |  |  |  | 
| 206 |  |  |  |  |  |  | L | 
| 207 |  |  |  |  |  |  |  | 
| 208 |  |  |  |  |  |  | =head1 AUTHOR | 
| 209 |  |  |  |  |  |  |  | 
| 210 |  |  |  |  |  |  | Joel Berger, Ejoel.a.berger@gmail.comE | 
| 211 |  |  |  |  |  |  |  | 
| 212 |  |  |  |  |  |  | =head1 CONTRIBUTORS | 
| 213 |  |  |  |  |  |  |  | 
| 214 |  |  |  |  |  |  | [None yet] | 
| 215 |  |  |  |  |  |  |  | 
| 216 |  |  |  |  |  |  | =head1 COPYRIGHT AND LICENSE | 
| 217 |  |  |  |  |  |  |  | 
| 218 |  |  |  |  |  |  | Copyright (C) 2017 by L and L. | 
| 219 |  |  |  |  |  |  |  | 
| 220 |  |  |  |  |  |  | This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. |