File Coverage

blib/lib/Articulate/Storage/DBIC/Simple.pm
Criterion Covered Total %
statement 87 98 88.7
branch 17 28 60.7
condition n/a
subroutine 21 25 84.0
pod 13 19 68.4
total 138 170 81.1


line stmt bran cond sub pod time code
1             package Articulate::Storage::DBIC::Simple;
2 2     2   4619 use strict;
  2         3  
  2         80  
3 2     2   11 use warnings;
  2         4  
  2         53  
4            
5 2     2   9 use Moo;
  2         4  
  2         15  
6             with 'Articulate::Role::Component';
7 2     2   753 use Articulate::Syntax;
  2         5  
  2         19  
8 2     2   3427 use JSON;
  2         5  
  2         18  
9 2     2   307 use Scalar::Util qw(blessed);
  2         3  
  2         3276  
10            
11             =head1 NAME
12            
13             Articulate::Content::DBIC::Simple - store your content in a simple database
14            
15             =cut
16            
17             =head1 DESCRIPTION
18            
19             This content storage interface works by placing content and metadata in a database table, to which it connects using L.
20            
21             All content items are stored in a single table defined in L, and rows contain meta, content and location. Meta is stored in JSON.
22            
23             It is left up to the application, not the database to maintain referential integrity (although there is a rudimentary cascade deletion for descendant items).
24            
25             On the other hand, you can make changes to your dat structure freely without making schema changes.
26            
27             By default, this will create an SQLite database in memory and deploy the schema (i.e. no persistence), but you can alter this using the C attribute. You can also make your own schema, provided it is a superset of the existing schema.
28            
29             =cut
30            
31             =head1 ATTRIBUTE
32            
33             =head3 schema
34            
35             components:
36             Articulate::Storage::DBIC::Simple:
37             schema:
38             class: Articulate::Storage::DBIC::Simple::Schema
39             constructor: connect
40             args:
41             - dbi:SQLite:somefile.db
42             - user_name
43             - notverysecretpassword
44            
45             Allows you to specify how to connect to your database. By default, it connects to an SQLite :memory: DB and uses the connect_and_deploy constructor from the L schema.
46            
47             =cut
48            
49             has schema => (
50             is => 'rw',
51             default => sub {
52             return {
53             class => 'Articulate::Storage::DBIC::Simple::Schema',
54             constructor => 'connect_and_deploy',
55             args => [
56             'dbi:SQLite::memory:', '',''
57             ],
58             }
59             },
60             coerce => sub {
61             instantiate $_[0],
62             },
63             );
64            
65            
66             =head1 METHODS
67            
68             =cut
69            
70             sub dbic_find { # internal method
71 18     18 0 36 my $self = shift;
72 18         33 my $location = shift;
73 18         412 my $dbic_item = $self->schema->resultset('Articulate::Item')->find( { location => "$location" } );
74             }
75            
76             sub dbic_to_real { # internal method
77 3     3 0 8 my $self = shift;
78 3         5 my $dbic_item = shift;
79 3         16 return $self->construction->construct( {
80             location => $dbic_item->location,
81             meta => from_json($dbic_item->meta),
82             content => $dbic_item->content,
83             } );
84             }
85            
86             sub real_to_dbic { # internal method
87 3     3 0 8 my $self = shift;
88 3         7 my $item = shift;
89 3         92 my $dbic_item = $self->schema->resultset('Articulate::Item')->new_result( {
90             location => '' . $item->location,
91             content => '' . $item->content,
92             meta => to_json ( $item->meta ),
93             } );
94             }
95            
96            
97             =head3 get_item
98            
99             $storage->get_item( 'zone/public/article/hello-world' )
100            
101             Retrieves the metadata for the content at that location.
102            
103             =cut
104            
105             sub get_item {
106 2     2 1 2356 my $self = shift;
107 2         11 my $location = shift->location;
108 2 50       11 throw_error Internal => "Bad location $location" unless $self->navigation->valid_location( $location );
109 2 50       8 my $dbic_item = $self->dbic_find ($location) or throw_error NotFound => "No item at $location";
110 2         4297 return $self->dbic_to_real( $dbic_item );
111             }
112            
113             =head3 get_meta
114            
115             $storage->get_meta( 'zone/public/article/hello-world' )
116            
117             Retrieves the metadata for the content at that location.
118            
119             =cut
120            
121             sub get_meta {
122 2     2 1 10 my $self = shift;
123 2         3 my $item = shift;
124 2         32 my $location = $item->location;
125 2 50       17 throw_error Internal => "Bad location $location" unless $self->navigation->valid_location( $location );
126 2 50       8 my $dbic_item = $self->dbic_find ($location) or throw_error NotFound => "No item at $location";
127 0         0 return $self->dbic_to_real( $dbic_item )->meta;
128             }
129            
130             =head3 set_meta
131            
132             $storage->set_meta( 'zone/public/article/hello-world', {...} )
133            
134             Sets the metadata for the content at that location.
135            
136             =cut
137            
138             sub set_meta {
139 1     1 1 8 my $self = shift;
140 1         2 my $item = shift;
141 1         17 my $location = $item->location;
142 1 50       9 throw_error Internal => "Bad location $location" unless $self->navigation->valid_location( $location );
143 1 50       6 my $dbic_item = $self->dbic_find ($location) or throw_error NotFound => "No item at $location";
144 0         0 $dbic_item->meta( to_json( $item->meta ) );
145 0         0 $dbic_item->update;
146 0         0 return $item;
147             }
148            
149             =head3 patch_meta
150            
151             $storage->patch_meta( 'zone/public/article/hello-world', {...} )
152            
153             Alters the metadata for the content at that location. Existing keys are retained.
154            
155             CURRENTLY this affects top-level keys only, but a descent algorigthm is planned.
156            
157             =cut
158            
159             sub patch_meta {
160 0     0 1 0 die 'not implemented'
161             }
162            
163            
164             =head3 get_settings
165            
166             $storage->get_settings('zone/public/article/hello-world')
167            
168             Retrieves the settings for the content at that location.
169            
170             =cut
171            
172             sub get_settings {
173 0     0 1 0 die 'not implemented'
174             }
175            
176             =head3 set_settings
177            
178             $storage->set_settings('zone/public/article/hello-world', $amended_settings)
179            
180             Retrieves the settings for the content at that location.
181            
182             =cut
183            
184             sub set_settings {
185 0     0 1 0 die 'not implemented'
186             }
187            
188             =head3 get_settings_complete
189            
190             $storage->get_settings_complete('zone/public/article/hello-world')
191            
192             Retrieves the settings for the content at that location.
193            
194             =cut
195            
196             sub get_settings_complete {
197 0     0 1 0 die 'not implemented'
198             }
199            
200            
201             =head3 get_content
202            
203             $storage->get_content('zone/public/article/hello-world')
204            
205             Retrieves the content at that location.
206            
207             =cut
208            
209             sub get_content {
210 3     3 1 21 my $self = shift;
211 3         5 my $item = shift;
212 3         36 my $location = $item->location;
213 3 50       22 throw_error Internal => "Bad location $location" unless $self->navigation->valid_location( $location );
214 3 100       17 my $dbic_item = $self->dbic_find ($location) or throw_error NotFound => "No item at $location";
215 1         2637 return $self->dbic_to_real( $dbic_item )->content;
216             }
217            
218             =head3 set_content
219            
220             $storage->set_content('zone/public/article/hello-world', $blob);
221            
222             Places content at that location.
223            
224             =cut
225            
226             sub set_content {
227 1     1 1 6 my $self = shift;
228 1         2 my $item = shift;
229 1         16 my $location = $item->location;
230 1 50       10 throw_error Internal => "Bad location $location" unless $self->navigation->valid_location( $location );
231 1 50       4 my $dbic_item = $self->dbic_find ($location) or throw_error NotFound => "No item at $location";
232 0         0 $dbic_item->content($item->content);
233 0         0 $dbic_item->update;
234 0         0 return $item;
235             }
236             =head3 create_item
237            
238             $storage->create_item('zone/public/article/hello-world', $meta, $blob);
239            
240             Places meta and content at that location.
241            
242             =cut
243            
244             sub create_item {
245 4     4 0 5500 my $self = shift;
246 4         7 my $item = shift;
247 4         92 my $location = $item->location;
248 4 50       38 throw_error Internal => "Bad location ".$location unless $self->navigation->valid_location( $location );
249 4 100       17 throw_error AlreadyExists => "Cannot create: item already exists at ".$location if $self->item_exists($location);
250 3         5871 my $dbic_item = $self->real_to_dbic ( $item );
251 3         728 $dbic_item->insert();
252 3         4728 return $item;
253             }
254            
255             =head3 item_exists
256            
257             if ($storage->item_exists( 'zone/public/article/hello-world')) {
258             ...
259             }
260            
261             Determines if the item has been created (only the C is tested).
262            
263             =cut
264            
265            
266             sub item_exists {
267 9     9 1 4254 my $self = shift;
268 9         39 my $location = shift->location;
269 9 50       40 throw_error Internal => "Bad location $location" unless $self->navigation->valid_location( $location );
270 9         42 !! $self->dbic_find( $location );
271             }
272            
273             =head3 list_items
274            
275             $storage->list_items ('/zone/public'); # 'hello-world', 'second-item' )
276            
277             Returns a list of items in the.
278            
279             =cut
280            
281            
282             sub list_items {
283 2     2 1 65 my $self = shift;
284 2         5 my $item = shift;
285 2         11 my $location = $item->location;
286 2         9 my $qm_location = $item->location;
287 2         64 my $dbic_items = $self->schema->resultset('Articulate::Item')->search( { location => { like => $qm_location.'%' } } );
288 2         521 my $locspec = locspec ( $location.'/*' );
289 2         79 return map { $_->[-1] } grep { $locspec->matches( $_ ); } map { loc( $_->location ) } $dbic_items->all;
  2         6  
  3         63  
  3         4964  
290             }
291            
292             sub get_content_cached {
293 1     1 0 8 my $self = shift;
294 1         3 $self->get_content(@_);
295             }
296            
297             sub get_meta_cached {
298 1     1 0 7 my $self = shift;
299 1         3 $self->get_meta(@_);
300             }
301            
302            
303             =head3 empty_all_content
304            
305             $storage->empty_all_content;
306            
307             Removes all content. This is totally irreversible, unless you took a backup!
308            
309             =cut
310            
311             sub empty_all_content {
312 1     1 1 3553 my $self = shift;
313 1         26 $self->schema->resultset('Articulate::Item')->delete();
314             }
315            
316             =head3 delete_item
317            
318             $storage->delete_item ('/zone/public');
319            
320             Deletes the item and all its descendants.
321            
322             =cut
323            
324             sub delete_item {
325 2     2 1 1470 my $self = shift;
326 2         4 my $item = shift;
327 2         29 my $qm_location = $item->location;
328 2         46 my $dbic_items = $self->schema->resultset('Articulate::Item')->search( { location => { like => "$qm_location\%" } } );
329             # todo: fix case of "foo" matches "foobar"
330 2 100       491 $dbic_items->count or throw_error NotFound => 'Item does not exist at '.$item->location;
331 1         4065 $dbic_items->delete();
332             }
333            
334             =head1 SEE ALSO
335            
336             =over
337            
338             =item * L
339            
340             =item * L
341            
342             =item * L
343            
344             =item * L
345            
346             =item * L
347            
348             =back
349            
350             =cut
351            
352             1;