First try to design a new interface for persistent data.
[onis.git] / lib / Onis / Data / Persistent.pm
1 package Onis::Data::Persistent;
2
3 use strict;
4 use warnings;
5
6 use Carp qw(confess);
7
8 =head1 NAME
9
10 Onis::Data::Persistent - Interface for storage backends
11
12 =head1 DESCRIPTION
13
14 Abstraction layer for modules that act as a backend and are able to store
15 internal data for longer than one run..
16
17 =cut
18
19 use Onis::Config qw#get_config get_checksum#;
20
21 @Onis::Data::Persistent::EXPORT_OK = qw##;
22 @Onis::Data::Persistent::ISA = ('Exporter');
23
24 our $StoreModule = 'None';
25
26 =head1 CONFIGURATION OPTIONS
27
28 Since this is a B<interface> the options are very few. One, to be specific. See
29 your favorite backend's documentation on it's options..
30
31 =over 4
32
33 =item B<storage_module>
34
35 Selects the storage module to use. Defaults to I<None> which is a dummy module
36 that doesn't do anything with the data.. (Other than storing it internally..)
37 Currently implemented options are:
38
39     None       (todo)
40     Storable   (maybe)
41     GDBM/SDBM  (todo)
42     MySQL      (todo)
43     PostgreSQL (maybe)
44
45 =back
46
47 =cut
48
49 if (get_config ('storage_module'))
50 {
51         $StoreModule = ucfirst (lc (get_config ('storage_module')));
52 }
53
54 {
55         my $mod_name = "Onis::Data::Persistent::$StoreModule";
56
57         eval qq(use $mod_name;);
58
59         if ($@)
60         {
61                 print STDERR $/, __FILE__, ": Could not load storage module ``$StoreModule''. Are you sure it exists?";
62                 exit (1);
63         }
64
65         unshift (@Onis::Data::Persistent::ISA, $mod_name);
66 }
67
68 =head1 INTERFACE
69
70 The child-modules have to provide the following interface:
71
72 =over 4
73
74 =item B<Onis::Data::Persistent-E<gt>new> (I<$name>, I<$key_name>, I<@field_names>)
75
76 This is the constructor for the objects that will hold the data. Some modules
77 may need a name for each field, and this is where plugins have to give the name
78 of each field. This is particularly important for backends using relational
79 databeses. I<$name> is merely a name for that variable or, in the database
80 world - a table. The name must be unique for each calling method's namespace.
81
82 Since this is a constructor it returns an object. The object "knows" the folling methods:
83
84 =item B<$data-E<gt>put> (I<$name>, I<$key>, I<@fields>)
85
86 Stores the given values in the data structure. How this is done is described
87 below in L<another paragraph>. Doesn't return anything. The number of entries
88 in I<@fields> has to match the number of entries in I<@field_names> when
89 creating the object using B<new>.
90
91 =item B<$data-E<gt>get> (I<$name>, I<$key>) 
92
93 Returns the data associated with the given I<$name>/I<$key> pair or an empty
94 list if no data has been stored under this tupel before..
95
96 =back
97
98 =head1 INTERNALS
99
100 The B<put> and B<get> methods can be found in the
101 B<Onis::Data::Persistent::None> module. Other modules are encouraged to inherit
102 from that module, but don't need to. The data is stored as follows: The object
103 that's returned to the caller is actually a hash with this layout:
104
105     %object =
106     (
107         data =>
108         {
109             key0 => [ qw(field0 field1 field2 ...) ],
110             key1 => [ qw(field0 field1 field2 ...) ],
111             key2 => [ qw(field0 field1 field2 ...) ],
112             ...
113         }
114     );
115
116 The actual data is not directly stored under I<%object> so database backends
117 can store metadata there (table name, credentials, whatever..).
118
119 =head1 FURTHER CONSIDERATIONS
120
121 Backend modules will probably read the entire data at startup and save
122 everything at the end. Another strategy might be reading (at least trying to)
123 an entry when it's first tried to B<get>..
124
125 Another problem might be if/when a module needs a list of keys. I think almost
126 all plugins need this.. I'll probably add a B<keys> method to which you can
127 tell which field to use for sorting.. That might make it a lot easier for
128 database-backends..
129
130 =head1 AUTHOR
131
132 Florian octo Forster, L<octo@verplant.org>. Any comments welcome as long as I
133 haven't started implementing this ;)
134
135 =cut
136
137 exit (0);