source: bookmarks/trunk/lib/Bookmarks/Controller.pm @ 88

Last change on this file since 88 was 88, checked in by peter, 9 years ago

Better separation of the model for searching and the web app controller/representation layer

  • renamed Bookmarks::get_bookmarks() to search()
  • Bookmarks::search() now returns a Bookmarks::Search object instead of a Bookmarks::List
  • search results now reside in the Bookmarks::Search class
File size: 6.5 KB
RevLine 
[70]1package Bookmarks::Controller;
2
[59]3use Moose;
[5]4
5use Encode;
[70]6use HTTP::Date qw{time2str str2time};
[5]7use JSON;
8use Bookmarks;
[88]9use Bookmarks::List;
[7]10use URI;
[59]11use Template;
[7]12
[61]13has dbname => (
14    is => 'ro',
15    required => 1,
16);
[59]17has bookmarks => (
[61]18    is => 'ro',
[59]19    handles => [qw{get_bookmark}],
[61]20    builder => '_build_bookmarks',
21    lazy => 1,
[59]22);
23has base_uri => (
24    is => 'ro',
25    builder => '_build_base_uri',
26    lazy => 1,
27);
28has request => (
29    is => 'ro',
[61]30    required => 1,
[59]31);
32
[61]33sub _build_bookmarks {
34    my $self = shift;
35    return Bookmarks->new({
36        dbname   => $self->dbname,
37        base_uri => $self->base_uri,
38    });
39}
40
[59]41sub _build_base_uri {
[5]42    my $self = shift;
[59]43    my $url = $self->request->base;
[41]44
[53]45    $url .= '/' unless $url =~ m{/$};
[59]46    return URI->new($url);
[5]47}
48
[59]49sub find_or_new {
[5]50    my $self = shift;
51
[59]52    my $bookmark = $self->bookmarks->get_bookmark({ uri => $self->request->param('uri') });
53    if ($bookmark) {
54        # redirect to the view of the existing bookmark
55        return [301, [Location => $bookmark->bookmark_uri], []];
56    } else {
57        # bookmark was not found; show the form to create a new bookmark
58        my $template = Template->new;
59        $template->process(
60            'bookmark.tt',
[66]61            { 
62                bookmark => {
63                    uri   => $self->request->param('uri'),
64                    title => $self->request->param('title') || '',
65                },
[59]66            },
67            \my $output,
68        );
69        return [404, ['Content-Type' => 'text/html; charset=UTF-8'], [$output]];
[5]70    }
[59]71}
[5]72
[59]73sub list {
74    my $self = shift;
75
[5]76    # list all the bookmarks
[59]77    my $mtime = $self->bookmarks->get_last_modified_time;
[57]78
[59]79    my $format = $self->request->param('format') || 'html';
[35]80
[59]81    my @tags = grep { $_ ne '' } $self->request->param('tag');
82    my $query = $self->request->param('q');
83    my $limit = $self->request->param('limit');
84    my $offset = $self->request->param('offset');
[68]85
[88]86    my $list = Bookmarks::List->new({
87        bookmarks => $self->bookmarks,
88        search    => $self->bookmarks->search({
89            query  => $query,
90            tags   => \@tags,
91            limit  => $limit,
92            offset => $offset,
93        }),
[13]94    });
[5]95
[68]96    my $as_format = "as_$format";
97    if (!$list->meta->has_method($as_format)) {
98        return [406, ['Content-Type' => 'text/plain; charset=UTF-8'], [qq{"$format" is not a supported format}]];
[5]99    }
[68]100    return $list->$as_format;
[5]101}
102
[9]103sub feed {
104    my $self = shift;
105
[59]106    my $query = $self->request->param('q');
107    my @tags = grep { $_ ne '' } $self->request->param('tag');
[31]108
[9]109    # construct a feed from the most recent 12 bookmarks
[88]110    my $list = Bookmarks::List->new({
111        bookmarks => $self->bookmarks,
112        search    => $self->bookmarks->search({ query => $query, tags => \@tags, limit => 12 }),
113    });
[68]114    return $list->as_atom;
[9]115}
116
[62]117# returns 1 if there is an If-Modified-Since header and it is newer than the given $mtime
118# returns 0 if there is an If-Modified-Since header but the $mtime is newer
119# returns undef if there is no If-Modified-Since header
[67]120sub _request_is_newer_than {
[62]121    my $self = shift;
122    my $mtime = shift;
123
124    # check If-Modified-Since header to return cache response
125    if ($self->request->env->{HTTP_IF_MODIFIED_SINCE}) {
126        my $cache_time = str2time($self->request->env->{HTTP_IF_MODIFIED_SINCE});
127        return $mtime <= $cache_time ? 1 : 0;
128    } else {
129        return;
130    }
131}
132
[5]133sub view {
[59]134    my ($self, $id) = @_;
[5]135
[59]136    my $format = $self->request->param('format') || 'html';
137
138    my $bookmark = $self->get_bookmark({ id => $id });
[5]139    if ($bookmark) {
[67]140        return [304, [], []] if $self->_request_is_newer_than($bookmark->mtime);
[62]141
[59]142        my $last_modified = time2str($bookmark->mtime);
[57]143       
[30]144        if ($format eq 'json') {
[59]145            my $json = decode_utf8(JSON->new->utf8->convert_blessed->encode($bookmark));
146            return [200, ['Content-Type' => 'application/json; charset=UTF-8', 'Last-Modified' => $last_modified], [$json]];
[5]147        } else {
[30]148            # display the bookmark form for this bookmark
[59]149            my $template = Template->new;
150            $template->process(
[30]151                'bookmark.tt',
[66]152                { bookmark => $bookmark },
[59]153                \my $output,
[30]154            );
[59]155            return [200, ['Content-Type' => 'text/html; charset=UTF-8', 'Last-Modified' => $last_modified], [$output]];
[30]156        }
157    } else {
[74]158        return [404, ['Content-Type' => 'text/plain; charset=UTF-8'], ["Bookmark $id not found"]];
[30]159    }
160}
161
162sub view_field {
[59]163    my ($self, $id, $field) = @_;
[30]164
[62]165    my $bookmark = $self->get_bookmark({ id => $id });
[30]166    if ($bookmark) {
[67]167        return [304, [], []] if $self->_request_is_newer_than($bookmark->mtime);
[62]168
[63]169        # check whether the requested field is part of the bookmark
170        if (!$bookmark->meta->has_attribute($field)) {
171            return [404, ['Content-Type' => 'text/plain; charset=UTF-8'], [qq{"$field" is not a valid bookmark data field}]];
[5]172        }
[63]173
174        # respond with just the requested field as plain text
175        my $value = $bookmark->$field;
[62]176        my $last_modified = time2str($bookmark->mtime);
177        return [200, ['Content-Type' => 'text/plain; charset=UTF-8', 'Last-Modified' => $last_modified], [ref $value eq 'ARRAY' ? join(' ', @{ $value }) : $value]];
[5]178    } else {
[74]179        return [404, ['Content-Type' => 'text/plain; charset=UTF-8'], ["Bookmark $id not found"]];
[5]180    }
181}
182
[63]183sub create_and_redirect {
[5]184    my $self = shift;
[59]185
186    my $uri   = $self->request->param('uri');
187    my $title = $self->request->param('title');
188    my @tags  = split ' ', $self->request->param('tags');
189
190    my $bookmark = $self->bookmarks->add({
[5]191        uri   => $uri,
192        title => $title,
193        tags  => \@tags,
194    });
195
[59]196    #TODO: not RESTful; the proper RESTful response would be a 201
197    return [303, ['Location' => $bookmark->bookmark_uri->canonical], []];
[5]198}
199
[63]200sub update_and_redirect {
[45]201    my $self = shift;
[59]202    my $id = shift;
[45]203
[59]204    my $bookmark = $self->bookmarks->get_bookmark({ id => $id });
[45]205    if ($bookmark) {
206        # update the URI, title, and tags
[59]207        $bookmark->uri($self->request->param('uri'));
208        $bookmark->title($self->request->param('title'));
209        $bookmark->tags([ split ' ', $self->request->param('tags') || '' ]);
[45]210
[46]211        # write to the database
[75]212        $bookmark->update;
[46]213
[59]214        #TODO: not RESTful; proper response would be a 200
215        return [303, ['Location' => $bookmark->bookmark_uri->canonical], []];
[45]216    } else {
[74]217        return [404, ['Content-Type' => 'text/plain; charset=UTF-8'], ["Bookmark $id not found"]];
[45]218    }
219}
220
[5]2211;
Note: See TracBrowser for help on using the repository browser.