| 1 | ################################################################################ |
1 | ################################################################################ |
| 2 | # WeBWorK mod_perl (c) 2000-2002 WeBWorK Project |
2 | # WeBWorK mod_perl (c) 2000-2002 WeBWorK Project |
| 3 | # $Id$ |
3 | # $Id$ |
| 4 | ################################################################################ |
4 | ################################################################################ |
| 5 | |
5 | |
| 6 | package WeBWorK::DB::Schema::Classlist1Hash; |
6 | package WeBWorK::DB::Schema::WW1Hash; |
| 7 | |
7 | |
| 8 | =head1 NAME |
8 | =head1 NAME |
| 9 | |
9 | |
| 10 | WeBWorK::DB::Schema::Classlist1Hash - support access to the user table with a |
10 | WeBWorK::DB::Schema::WW1Hash - support access to the set_user and problem_user |
| 11 | 1.x-structured hash-style backend. |
11 | tables with a WWDBv1 hash-style backend. |
| 12 | |
12 | |
| 13 | =cut |
13 | =cut |
| 14 | |
14 | |
| 15 | use strict; |
15 | use strict; |
| 16 | use warnings; |
16 | use warnings; |
| 17 | use WeBWorK::DB::Record::User; |
17 | use Data::Dumper; |
| 18 | use WeBWorK::DB::Utils qw(record2hash hash2record hash2string string2hash); |
18 | use WeBWorK::DB::Utils qw(record2hash hash2record hash2string string2hash); |
| 19 | |
19 | |
| 20 | use constant TABLES => qw(user); |
20 | use constant TABLES => qw(set_user problem_user); |
| 21 | use constant STYLE => "hash"; |
21 | use constant STYLE => "hash"; |
|
|
22 | |
|
|
23 | use constant LOGIN_PREFIX => "login<>"; |
|
|
24 | use constant SET_PREFIX => "set<>"; |
|
|
25 | use constant MAX_PSVN_GENERATION_ATTEMPTS => 200; |
| 22 | |
26 | |
| 23 | ################################################################################ |
27 | ################################################################################ |
| 24 | # static functions |
28 | # static functions |
| 25 | ################################################################################ |
29 | ################################################################################ |
| 26 | |
30 | |
| … | |
… | |
| 35 | ################################################################################ |
39 | ################################################################################ |
| 36 | # constructor |
40 | # constructor |
| 37 | ################################################################################ |
41 | ################################################################################ |
| 38 | |
42 | |
| 39 | sub new($$$) { |
43 | sub new($$$) { |
| 40 | my ($proto, $driver, $table) = @_; |
44 | my ($proto, $driver, $table, $record, $params) = @_; |
| 41 | my $class = ref($proto) || $proto; |
45 | my $class = ref($proto) || $proto; |
| 42 | die "$table: unsupported table" |
46 | die "$table: unsupported table" |
| 43 | unless grep { $_ eq $table } $proto->tables(); |
47 | unless grep { $_ eq $table } $proto->tables(); |
| 44 | die $driver->style(), ": style mismatch" |
48 | die $driver->style(), ": style mismatch" |
| 45 | unless $driver->style() eq $proto->style(); |
49 | unless $driver->style() eq $proto->style(); |
| 46 | my $self = { |
50 | my $self = { |
| 47 | driver => $driver, |
51 | driver => $driver, |
| 48 | table => $table, |
52 | table => $table, |
|
|
53 | record => $record, |
|
|
54 | params => $params, |
| 49 | }; |
55 | }; |
| 50 | bless $self, $class; |
56 | bless $self, $class; |
| 51 | return $self; |
57 | return $self; |
| 52 | } |
58 | } |
| 53 | |
59 | |
| 54 | ################################################################################ |
60 | ################################################################################ |
| 55 | # table access functions |
61 | # table access functions |
| 56 | ################################################################################ |
62 | ################################################################################ |
| 57 | |
63 | |
| 58 | sub list($) { |
64 | sub list($@) { |
|
|
65 | my ($self, @keyparts) = @_; |
|
|
66 | my ($matchUserID, $matchSetID) = @keyparts[0 .. 1]; |
|
|
67 | my @matchingPSVNs; |
|
|
68 | if (defined $matchUserID and not defined $matchSetID) { |
|
|
69 | @matchingPSVNs = $self->getPSVNsForUser($matchUserID); |
|
|
70 | } elsif (defined $matchSetID and not defined $matchUserID) { |
|
|
71 | @matchingPSVNs = $self->getPSVNsForSet($matchSetID); |
|
|
72 | } elsif (defined $matchUserID and defined $matchSetID) { |
|
|
73 | @matchingPSVNs = $self->getPSVN($matchUserID, $matchSetID); |
|
|
74 | } else { |
|
|
75 | return unless $self->{driver}->connect("ro"); |
|
|
76 | @matchingPSVNs = |
|
|
77 | grep { m/^\d+$/ } |
|
|
78 | keys %{ $self->{driver}->hash() }; |
|
|
79 | } |
|
|
80 | my @result; |
|
|
81 | return () unless $self->{driver}->connect("ro"); |
|
|
82 | if ($self->{table} eq "set_user") { |
|
|
83 | foreach (@matchingPSVNs) { |
|
|
84 | my $string = $self->{driver}->hash()->{$_}; |
|
|
85 | my $UserSet = $self->string2records($string); |
|
|
86 | push @result, [$UserSet->user_id(), $UserSet->set_id()]; |
|
|
87 | } |
|
|
88 | } elsif ($self->{table} eq "problem_user") { |
|
|
89 | foreach (@matchingPSVNs) { |
|
|
90 | my $string = $self->{driver}->hash()->{$_}; |
|
|
91 | my (undef, @UserProblems) = $self->string2records($string); |
|
|
92 | foreach (@UserProblems) { |
|
|
93 | push @result, [$_->user_id(), $_->set_id(), |
|
|
94 | $_->problem_id()]; |
|
|
95 | } |
|
|
96 | } |
|
|
97 | } |
|
|
98 | $self->{driver}->disconnect(); |
|
|
99 | return @result; |
|
|
100 | } |
|
|
101 | |
|
|
102 | sub exists($@) { |
|
|
103 | my ($self, @keyparts) = @_; |
|
|
104 | my ($userID, $setID) = @keyparts[0 .. 1]; |
|
|
105 | my $PSVN = $self->getPSVN($userID, $setID); |
|
|
106 | if ($self->{table} eq "set_user") { |
|
|
107 | return $PSVN; |
|
|
108 | } elsif ($self->{table} eq "problem_user") { |
|
|
109 | my $problemID = $keyparts[2]; |
|
|
110 | my $string = $self->fetchString($PSVN); |
|
|
111 | my (undef, @Problems) = $self->string2records($string); |
|
|
112 | return grep { $_->problem_id() eq $problemID } @Problems; |
|
|
113 | # optimization, if IDs are guaranteed to be numeric |
|
|
114 | # and in order: return (@Problems >= $keyparts[2]) |
|
|
115 | } |
|
|
116 | } |
|
|
117 | |
|
|
118 | sub add($$) { |
|
|
119 | my ($self, $Record) = @_; |
|
|
120 | my $userID = $Record->user_id(); |
|
|
121 | my $setID = $Record->set_id(); |
|
|
122 | if ($self->{table} eq "set_user") { |
|
|
123 | die "($userID, $setID): UserSet exists.\n" |
|
|
124 | if $self->getPSVN($userID, $setID); |
|
|
125 | my $PSVN = $self->setPSVN($userID, $setID); |
|
|
126 | my $string = $self->records2string($Record); # no problems |
|
|
127 | $self->storeString($PSVN, $string); |
|
|
128 | } elsif ($self->{table} eq "problem_user") { |
|
|
129 | my $problemID = $Record->problem_id(); |
|
|
130 | my $PSVN = $self->getPSVN($userID, $setID); |
|
|
131 | die "($userID, $setID): UserSet not found.\n" unless $PSVN; |
|
|
132 | my $string = $self->fetchString($PSVN); |
|
|
133 | my ($Set, @Problems) = $self->string2records($string); |
|
|
134 | die "($userID, $setID, $problemID): UserProblem exists.\n" |
|
|
135 | if grep { $_->problem_id() eq $problemID } @Problems; |
|
|
136 | push @Problems, $Record; |
|
|
137 | $string = $self->records2string($Set, @Problems); |
|
|
138 | $self->storeString($PSVN, $string); |
|
|
139 | } |
|
|
140 | } |
|
|
141 | |
|
|
142 | sub get($@) { |
|
|
143 | my ($self, @keyparts) = @_; |
|
|
144 | my ($userID, $setID) = @keyparts[0 .. 1]; |
|
|
145 | die "userID not specified." unless defined $userID; |
|
|
146 | die "setID not specified." unless defined $setID; |
|
|
147 | my $PSVN = $self->getPSVN($userID, $setID); |
|
|
148 | return undef unless $PSVN; |
|
|
149 | my $string = $self->fetchString($PSVN); |
|
|
150 | if ($self->{table} eq "set_user") { |
|
|
151 | my $UserSet = $self->string2records($string); |
|
|
152 | $UserSet->psvn($PSVN); |
|
|
153 | return $UserSet; |
|
|
154 | } if ($self->{table} eq "problem_user") { |
|
|
155 | my ($problemID) = $keyparts[2]; |
|
|
156 | die "problemID not specified." unless defined $problemID; |
|
|
157 | my (undef, @UserProblems) = $self->string2records($string); |
|
|
158 | return grep { $_->problem_id() eq $problemID } @UserProblems; |
|
|
159 | } |
|
|
160 | } |
|
|
161 | |
|
|
162 | sub put($$) { |
|
|
163 | my ($self, $Record) = @_; |
|
|
164 | my $userID = $Record->user_id(); |
|
|
165 | my $setID = $Record->set_id(); |
|
|
166 | my $PSVN = $self->getPSVN($userID, $setID); |
|
|
167 | die "($userID, $setID): UserSet not found.\n" unless $PSVN; |
|
|
168 | my $string = $self->fetchString($PSVN); |
|
|
169 | my ($Set, @Problems) = $self->string2records($string); |
|
|
170 | if ($self->{table} eq "set_user") { |
|
|
171 | $string = $self->records2string($Record, @Problems); |
|
|
172 | } elsif ($self->{table} eq "problem_user") { |
|
|
173 | my $problemID = $Record->problem_id(); |
|
|
174 | my $found = 0; |
|
|
175 | foreach (@Problems) { |
|
|
176 | if ($_->problem_id() eq $problemID) { |
|
|
177 | $found = 1; |
|
|
178 | $_ = $Record; |
|
|
179 | } |
|
|
180 | } |
|
|
181 | die "($userID, $setID, $problemID): UserProblem not found.\n" |
|
|
182 | unless $found; |
|
|
183 | $string = $self->records2string($Set, @Problems); |
|
|
184 | } |
|
|
185 | $self->storeString($PSVN, $string); |
|
|
186 | } |
|
|
187 | |
|
|
188 | sub delete($@) { |
|
|
189 | my ($self, @keyparts) = @_; |
|
|
190 | my ($userID, $setID) = @keyparts[0 .. 1]; |
|
|
191 | my $PSVN = $self->getPSVN($userID, $setID); |
|
|
192 | return 0 unless $PSVN; |
|
|
193 | if ($self->{table} eq "set_user") { |
|
|
194 | $self->deletePSVN($userID, $setID); |
|
|
195 | $self->deleteString($PSVN); |
|
|
196 | } elsif ($self->{table} eq "problem_user") { |
|
|
197 | my $problemID = $keyparts[2]; |
|
|
198 | my $string = $self->fetchString($PSVN); |
|
|
199 | my ($Set, @Problems) = $self->string2records($string); |
|
|
200 | my $length = @Problems; |
|
|
201 | @Problems = grep { not $_->problem_id() eq $problemID } @Problems; |
|
|
202 | return 0 if $length == @Problems; |
|
|
203 | $string = $self->records2string($Set, @Problems); |
|
|
204 | $self->storeString($PSVN, $string); |
|
|
205 | } |
|
|
206 | return 1; |
|
|
207 | } |
|
|
208 | |
|
|
209 | ################################################################################ |
|
|
210 | # table multiplexing functions |
|
|
211 | # both the set_user and problem_user tables are stored in one hash, keyed by |
|
|
212 | # PSVN. we need to be able to split a hash value into two records, and combine |
|
|
213 | # two records into a single hash value. |
|
|
214 | ################################################################################ |
|
|
215 | |
|
|
216 | # here's a little issue... the schema API seems to allow the user to specify |
|
|
217 | # what record class to use (per instance), but since WW1Hash has to monkey with |
|
|
218 | # multiple record types in the same instance, we have to hardcode record |
|
|
219 | # classes. this is fine, as long as no one tries to use non-default record |
|
|
220 | # classes. I guess this is bad, so: ***! |
|
|
221 | # NOTE: we can use the new params layout field to specify this. |
|
|
222 | sub string2records($$) { |
|
|
223 | my ($self, $string) = @_; |
|
|
224 | my %hash = string2hash($string); |
|
|
225 | my $UserSet = hash2record("WeBWorK::DB::Record::UserSet", %hash); |
|
|
226 | return $UserSet unless wantarray; |
|
|
227 | my @UserProblems; |
|
|
228 | foreach (grep { s/^pfn// } keys %hash) { |
|
|
229 | my %problemHash = ( |
|
|
230 | "stlg" => $hash{stlg}, |
|
|
231 | "stnm" => $hash{stnm}, |
|
|
232 | "#" => $_, |
|
|
233 | "pfn#" => $hash{"pfn$_"}, |
|
|
234 | "pva#" => $hash{"pva$_"}, |
|
|
235 | "pmia#" => $hash{"pmia$_"}, |
|
|
236 | "pse#" => $hash{"pse$_"}, |
|
|
237 | "pst#" => $hash{"pst$_"}, |
|
|
238 | "pat#" => $hash{"pat$_"}, |
|
|
239 | "pan#" => $hash{"pan$_"}, |
|
|
240 | "pca#" => $hash{"pca$_"}, |
|
|
241 | "pia#" => $hash{"pia$_"}, |
|
|
242 | ); |
|
|
243 | push @UserProblems, hash2record("WeBWorK::DB::Record::UserProblem", %problemHash); |
|
|
244 | } |
|
|
245 | return $UserSet, @UserProblems; |
|
|
246 | } |
|
|
247 | |
|
|
248 | sub records2string($$@) { |
|
|
249 | my ($self, $Set, @Problems) = @_; |
|
|
250 | my %hash = record2hash($Set); |
|
|
251 | foreach (@Problems) { |
|
|
252 | my %problemHash = record2hash($_); |
|
|
253 | my $n = $problemHash{"#"}; |
|
|
254 | foreach ('pfn#', 'pva#', 'pmia#', 'pse#', 'pst#', 'pat#', 'pan#', 'pca#', 'pia#') { |
|
|
255 | my $realKey = $_; |
|
|
256 | $realKey =~ s/#/$n/; |
|
|
257 | $hash{$realKey} = $problemHash{$_}; |
|
|
258 | } |
|
|
259 | } |
|
|
260 | return hash2string(%hash); |
|
|
261 | } |
|
|
262 | |
|
|
263 | ################################################################################ |
|
|
264 | # PSVN and index functions |
|
|
265 | # the PSVN pseudo-table and the set and user indexes are not visible to the |
|
|
266 | # API, but we need to be able to update them to remain compatible with WWDBv1. |
|
|
267 | ################################################################################ |
|
|
268 | |
|
|
269 | # retrieves a list of existing PSVNs from the user PSVN index |
|
|
270 | sub getPSVNsForUser($$) { |
|
|
271 | my ($self, $userID) = @_; |
|
|
272 | my $setsForUser = $self->fetchString(LOGIN_PREFIX.$userID); |
|
|
273 | return unless defined $setsForUser; |
|
|
274 | my %sets = string2hash($setsForUser); |
|
|
275 | return values %sets; |
|
|
276 | } |
|
|
277 | |
|
|
278 | # retrieves a list of existing PSVNs from the set PSVN index |
|
|
279 | sub getPSVNsForSet($$) { |
|
|
280 | my ($self, $setID) = @_; |
|
|
281 | my $usersForSet = $self->fetchString(SET_PREFIX.$setID); |
|
|
282 | return unless defined $usersForSet; |
|
|
283 | my %users = string2hash($usersForSet); |
|
|
284 | return values %users; |
|
|
285 | } |
|
|
286 | |
|
|
287 | # retrieves an existing PSVN from the PSVN indexes |
|
|
288 | sub getPSVN($$$) { |
|
|
289 | my ($self, $userID, $setID) = @_; |
|
|
290 | return unless $self->{driver}->connect("ro"); |
|
|
291 | my $setsForUser = $self->{driver}->hash()->{LOGIN_PREFIX.$userID}; |
|
|
292 | my $usersForSet = $self->{driver}->hash()->{SET_PREFIX.$setID}; |
|
|
293 | $self->{driver}->disconnect(); |
|
|
294 | # * if setsForUser is non-empty, then there are sets built for this |
|
|
295 | # user. |
|
|
296 | # * if usersForSet is non-empty, then this set has been built for at |
|
|
297 | # least one user. |
|
|
298 | # * if either are empty, it is guaranteed that this set has not been |
|
|
299 | # built for this user. |
|
|
300 | return unless defined $setsForUser and defined $usersForSet; #shut up, shut up, shut up! |
|
|
301 | return unless $setsForUser and $usersForSet; |
|
|
302 | my %sets = string2hash($setsForUser); |
|
|
303 | my %users = string2hash($usersForSet); |
|
|
304 | return unless exists $sets{$setID} and exists $users{$userID}; |
|
|
305 | # more sanity checks: the following should never happen. |
|
|
306 | # if they do, run screaming for the hills. |
|
|
307 | if (defined $sets{$setID} and not defined $users{$userID}) { |
|
|
308 | die "PSVN indexes inconsistent: set exists in user index ", |
|
|
309 | "but user does not exist in set index."; |
|
|
310 | } elsif (not defined $sets{$setID} and defined $users{$userID}) { |
|
|
311 | die "PSVN indexes inconsistent: user exists in set index ", |
|
|
312 | "but set does not exist in user index."; |
|
|
313 | } elsif ($sets{$setID} != $users{$userID}) { |
|
|
314 | die "PSVN indexes inconsistent: user index and set index ", |
|
|
315 | "gave different PSVN values."; |
|
|
316 | } |
|
|
317 | return $sets{$setID}; |
|
|
318 | } |
|
|
319 | |
|
|
320 | # generates a new PSVN, updates the PSVN indexes, returns the PSVN |
|
|
321 | # if there is already a PSVN for this pair, reuse it |
|
|
322 | sub setPSVN($$$) { |
|
|
323 | my ($self, $userID, $setID) = @_; |
|
|
324 | my $PSVN = $self->getPSVN($userID, $setID); |
|
|
325 | unless ($PSVN) { |
|
|
326 | # yeah, create a new PSVN here |
|
|
327 | my $min_psvn = 10**($self->{params}->{psvnLength} - 1); |
|
|
328 | my $max_psvn = 10**$self->{params}->{psvnLength} - 1; |
|
|
329 | my $attempts = 0; |
|
|
330 | do { |
|
|
331 | if (++$attempts > MAX_PSVN_GENERATION_ATTEMPTS) { |
|
|
332 | die "failed to find an unused PSVN within ", |
|
|
333 | MAX_PSVN_GENERATION_ATTEMPTS, " attempts."; |
|
|
334 | } |
|
|
335 | $PSVN = int(rand($max_psvn-$min_psvn+1)) + $min_psvn; |
|
|
336 | } while ($self->fetchString($PSVN)); |
|
|
337 | $self->{driver}->connect("rw"); # open "rw" to lock |
|
|
338 | # get current PSVN indexes |
|
|
339 | my $setsForUser = $self->{driver}->hash()->{LOGIN_PREFIX.$userID}; |
|
|
340 | my $usersForSet = $self->{driver}->hash()->{SET_PREFIX.$setID}; |
|
|
341 | my %sets = string2hash($setsForUser); # sets built for user $userID |
|
|
342 | my %users = string2hash($usersForSet); # users for which set $setID has been built |
|
|
343 | # insert new PSVN into each hash |
|
|
344 | $sets{$setID} = $PSVN; |
|
|
345 | $users{$userID} = $PSVN; |
|
|
346 | # re-encode the hashes |
|
|
347 | $setsForUser = hash2string(%sets); |
|
|
348 | $usersForSet = hash2string(%users); |
|
|
349 | # store 'em in the database |
|
|
350 | $self->{driver}->hash()->{LOGIN_PREFIX.$userID} = $setsForUser; |
|
|
351 | $self->{driver}->hash()->{SET_PREFIX.$setID} = $usersForSet; |
|
|
352 | $self->{driver}->disconnect(); |
|
|
353 | }; |
|
|
354 | return $PSVN; |
|
|
355 | } |
|
|
356 | |
|
|
357 | # remove an existing PSVN from the PSVN indexes |
|
|
358 | sub deletePSVN($$$) { |
|
|
359 | my ($self, $userID, $setID) = @_; |
|
|
360 | my $PSVN = $self->getPSVN($userID, $setID); |
|
|
361 | return unless $PSVN; |
|
|
362 | $self->{driver}->connect("rw"); # open "rw" to lock |
|
|
363 | my $setsForUser = $self->{driver}->hash()->{LOGIN_PREFIX.$userID}; |
|
|
364 | my $usersForSet = $self->{driver}->hash()->{SET_PREFIX.$setID}; |
|
|
365 | my %sets = string2hash($setsForUser); # sets built for user $userID |
|
|
366 | my %users = string2hash($usersForSet); # users for which set $setID has been built |
|
|
367 | delete $sets{$setID}; |
|
|
368 | delete $users{$userID}; |
|
|
369 | $setsForUser = hash2string(%sets); |
|
|
370 | $usersForSet = hash2string(%users); |
|
|
371 | if ($setsForUser) { |
|
|
372 | $self->{driver}->hash()->{LOGIN_PREFIX.$userID} = $setsForUser; |
|
|
373 | } else { |
|
|
374 | delete $self->{driver}->hash()->{LOGIN_PREFIX.$userID}; |
|
|
375 | } |
|
|
376 | if ($usersForSet) { |
|
|
377 | $self->{driver}->hash()->{SET_PREFIX.$setID} = $usersForSet; |
|
|
378 | } else { |
|
|
379 | delete $self->{driver}->hash()->{SET_PREFIX.$setID}; |
|
|
380 | } |
|
|
381 | $self->{driver}->disconnect(); |
|
|
382 | return 1; |
|
|
383 | } |
|
|
384 | |
|
|
385 | ################################################################################ |
|
|
386 | # hash string interface |
|
|
387 | ################################################################################ |
|
|
388 | |
|
|
389 | sub fetchString($$) { |
|
|
390 | my ($self, $PSVN) = @_; |
|
|
391 | $self->{driver}->connect("ro"); |
|
|
392 | my $string = $self->{driver}->hash()->{$PSVN}; |
|
|
393 | $self->{driver}->disconnect(); |
|
|
394 | return $string; |
|
|
395 | } |
|
|
396 | |
|
|
397 | |
|
|
398 | sub storeString($$$) { |
|
|
399 | my ($self, $PSVN, $string) = @_; |
|
|
400 | $self->{driver}->connect("rw"); |
|
|
401 | $self->{driver}->hash()->{$PSVN} = $string; |
|
|
402 | $self->{driver}->disconnect(); |
|
|
403 | } |
|
|
404 | |
|
|
405 | sub deleteString($$) { |
|
|
406 | my ($self, $PSVN) = @_; |
|
|
407 | $self->{driver}->connect("rw"); |
|
|
408 | delete $self->{driver}->hash()->{$PSVN}; |
|
|
409 | $self->{driver}->disconnect(); |
|
|
410 | } |
|
|
411 | |
|
|
412 | ################################################################################ |
|
|
413 | # debugging |
|
|
414 | ################################################################################ |
|
|
415 | |
|
|
416 | sub dumpDB($) { |
| 59 | my ($self) = @_; |
417 | my ($self) = @_; |
| 60 | $self->{driver}->connect("ro"); |
418 | $self->{driver}->connect("ro"); |
| 61 | my @keys = grep !/^>>/, keys %{ $self->{driver}->hash() }; |
419 | my $result = Dumper( $self->{driver}->hash() ); |
| 62 | $self->{driver}->disconnect(); |
|
|
| 63 | return @keys; |
|
|
| 64 | } |
|
|
| 65 | |
|
|
| 66 | sub exists($$) { |
|
|
| 67 | my ($self, $userID) = @_; |
|
|
| 68 | $self->{driver}->connect("ro"); |
420 | $self->{driver}->disconnect(); |
| 69 | my $exists = exists $self->{driver}->hash()->{$userID}; |
|
|
| 70 | $self->{driver}->disconnect(); |
|
|
| 71 | return $exists; |
421 | return $result; |
| 72 | } |
|
|
| 73 | |
|
|
| 74 | sub add($$) { |
|
|
| 75 | my ($self, $User) = @_; |
|
|
| 76 | $self->{driver}->connect("rw"); |
|
|
| 77 | my $hash = $self->{driver}->hash(); |
|
|
| 78 | die $User->id, ": user exists" if exists $hash->{$User->id}; |
|
|
| 79 | $hash->{$User->id} = hash2string(record2hash($User)); |
|
|
| 80 | $self->{driver}->disconnect(); |
|
|
| 81 | } |
|
|
| 82 | |
|
|
| 83 | sub get($$) { |
|
|
| 84 | my ($self, $userID) = @_; |
|
|
| 85 | $self->{driver}->connect("ro"); |
|
|
| 86 | my $string = $self->{driver}->hash()->{$userID}; |
|
|
| 87 | $self->{driver}->disconnect(); |
|
|
| 88 | return undef unless $string; |
|
|
| 89 | my $record = hash2record("WeBWorK::DB::Record::User", string2hash($string)); |
|
|
| 90 | $record->id($userID); |
|
|
| 91 | return $record; |
|
|
| 92 | } |
|
|
| 93 | |
|
|
| 94 | sub put($$) { |
|
|
| 95 | my ($self, $User) = @_; |
|
|
| 96 | $self->{driver}->connect("rw"); |
|
|
| 97 | my $hash = $self->{driver}->hash(); |
|
|
| 98 | die $User->id, ": user not found" unless exists $hash->{$User->id}; |
|
|
| 99 | $hash->{$User->id} = hash2string(record2hash($User)); |
|
|
| 100 | $self->{driver}->disconnect(); |
|
|
| 101 | } |
|
|
| 102 | |
|
|
| 103 | sub delete($$) { |
|
|
| 104 | my ($self, $userID) = @_; |
|
|
| 105 | $self->{driver}->connect("rw"); |
|
|
| 106 | my $hash = $self->{driver}->hash(); |
|
|
| 107 | die "$userID: user not found" unless exists $hash->{$userID}; |
|
|
| 108 | delete $hash->{$userID}; |
|
|
| 109 | $self->{driver}->disconnect(); |
|
|
| 110 | } |
422 | } |
| 111 | |
423 | |
| 112 | 1; |
424 | 1; |