| 1 | package WeBWorK::CourseEnvironment; |
1 | package WeBWorK::CourseEnvironment; |
| 2 | |
2 | |
|
|
3 | use strict; |
|
|
4 | use warnings; |
| 3 | use Safe; |
5 | use Safe; |
| 4 | |
6 | |
|
|
7 | # new($invocant, $webworkRoot, $courseName) |
|
|
8 | # $invocant implicitly set by caller |
|
|
9 | # $webworkRoot directory that contains the WeBWorK distribution |
|
|
10 | # $courseName name of the course being used |
| 5 | sub new { |
11 | sub new { |
| 6 | my $class = shift; |
12 | my $invocant = shift; |
|
|
13 | my $class = ref($invocant) || $invocant; |
| 7 | my $webworkRoot = shift; |
14 | my $webworkRoot = shift; |
| 8 | my $courseName = shift; |
15 | my $courseName = shift; |
|
|
16 | my $safe = Safe->new; |
|
|
17 | |
|
|
18 | # set up some defaults that the environment files will need |
|
|
19 | $safe->reval("\$webworkRoot = '$webworkRoot'"); |
|
|
20 | $safe->reval("\$courseName = '$courseName'"); |
| 9 | |
21 | |
| 10 | # determine location of globalEnvironmentFile |
22 | # determine location of globalEnvironmentFile |
| 11 | my $globalEnvironmentFile = "$webworkRoot/conf/global.conf"; |
23 | my $globalEnvironmentFile = "$webworkRoot/conf/global.conf"; |
| 12 | |
24 | |
| 13 | # read and evaluate the global environment file |
25 | # read and evaluate the global environment file |
| 14 | my $globalFileContents = readFile($globalEnvironmentFile); |
26 | my $globalFileContents = readFile($globalEnvironmentFile); |
| 15 | my %globalConf = Safe->new->reval($globalFileContents); |
27 | $safe->reval($globalFileContents); |
| 16 | |
28 | |
| 17 | # if that evaluation failed, we can't really go on -- we need a global environment! |
29 | # if that evaluation failed, we can't really go on... |
|
|
30 | # we need a global environment! |
| 18 | $@ and die "Could not evaluate global environment file $globalEnvironmentFile: $@"; |
31 | $@ and die "Could not evaluate global environment file $globalEnvironmentFile: $@"; |
| 19 | |
32 | |
| 20 | # determine location of courseEnvironmentFile |
33 | # determine location of courseEnvironmentFile |
| 21 | my $courseEnvironmentFile = $globalConf{coursesDirectory} |
34 | # pull it out of $safe's symbol table ad hoc |
| 22 | . "/$courseName/" |
35 | # (we don't want to do the hash conversion yet) |
| 23 | . $globalConf{courseEnvironmentFilename}; |
36 | no strict 'refs'; |
| 24 | |
37 | my $courseEnvironmentFile = ${*{${$safe->root."::"}{courseFiles}}}{environment}; |
|
|
38 | use strict 'refs'; |
|
|
39 | |
| 25 | # read and evaluate the course environment file |
40 | # read and evaluate the course environment file |
|
|
41 | # if readFile failed, we don't bother trying to reval |
| 26 | my $courseFileContents = readFile($courseEnvironmentFile); |
42 | my $courseFileContents = eval { readFile($courseEnvironmentFile) }; # catch exceptions |
| 27 | my %courseConf = Safe->new->reval($courseFileContents); |
43 | $@ or $safe->reval($courseFileContents); |
| 28 | |
44 | |
| 29 | # if that evaluation failed, we can't really go on -- we need a course environment! |
45 | # get the safe compartment's namespace as a hash |
| 30 | $@ and die "Could not evaluate course environment file $courseEnvironmentFile: $@"; |
46 | no strict 'refs'; |
| 31 | |
47 | my %symbolHash = %{$safe->root."::"}; |
| 32 | my $self = { %globalConf, %courseConf }; |
48 | use strict 'refs'; |
|
|
49 | |
|
|
50 | # convert the symbol hash into a hash of regular variables. |
|
|
51 | my $self = {}; |
|
|
52 | foreach my $name (keys %symbolHash) { |
|
|
53 | # weed out internal symbols |
|
|
54 | next if $name =~ /^(INC|_|__ANON__|main::)$/; |
|
|
55 | # pull scalar, array, and hash values for this symbol |
|
|
56 | my $scalar = ${*{$symbolHash{$name}}}; |
|
|
57 | my @array = @{*{$symbolHash{$name}}}; |
|
|
58 | my %hash = %{*{$symbolHash{$name}}}; |
|
|
59 | # for multiple variables sharing a symbol, scalar takes precedence |
|
|
60 | # over array, which takes precedence over hash. |
|
|
61 | if (defined $scalar) { |
|
|
62 | $self->{$name} = $scalar; |
|
|
63 | } elsif (@array) { |
|
|
64 | $self->{$name} = \@array; |
|
|
65 | } elsif (%hash) { |
|
|
66 | $self->{$name} = \%hash; |
|
|
67 | } |
|
|
68 | } |
|
|
69 | |
| 33 | bless $self, $class; |
70 | bless $self, $class; |
| 34 | return $self; |
71 | return $self; |
| 35 | } |
72 | } |
| 36 | |
73 | |
| 37 | sub get { |
74 | sub hash2string { |
| 38 | my $self = shift; |
75 | my $hr = shift; |
|
|
76 | my $indent = shift || 0; |
|
|
77 | my $result; |
|
|
78 | foreach (keys %$hr) { |
|
|
79 | $result .= "\t"x$indent . "{$_} ="; |
|
|
80 | if (ref $hr->{$_} eq 'HASH') { |
|
|
81 | $result .= "\n"; |
|
|
82 | $result .= hash2string($hr->{$_}, $indent+1); |
|
|
83 | } elsif (ref $hr->{$_} eq 'ARRAY') { |
|
|
84 | $result .= "\n"; |
|
|
85 | $result .= array2string($hr->{$_}, $indent+1); |
|
|
86 | } else { |
|
|
87 | $result .= " " . $hr->{$_} . "\n"; |
|
|
88 | } |
|
|
89 | } |
|
|
90 | return $result; |
|
|
91 | } |
|
|
92 | |
|
|
93 | sub array2string { |
| 39 | my $var = shift; |
94 | my $ar = shift; |
| 40 | return $self->{$var}; |
95 | my $indent = shift || 0; |
|
|
96 | my $result; |
|
|
97 | foreach (0 .. @$ar-1) { |
|
|
98 | $result .= "\t"x$indent . "[$_] ="; |
|
|
99 | if (ref $ar->[$_] eq 'HASH') { |
|
|
100 | $result .= "\n"; |
|
|
101 | $result .= hash2string($ar->[$_], $indent+1); |
|
|
102 | } elsif (ref $ar->[$_] eq 'ARRAY') { |
|
|
103 | $result .= "\n"; |
|
|
104 | $result .= array2string($ar->[$_], $indent+1); |
|
|
105 | } else { |
|
|
106 | $result .= " " . $ar->[$_] . "\n"; |
|
|
107 | } |
|
|
108 | } |
|
|
109 | return $result; |
| 41 | } |
110 | } |
| 42 | |
111 | |
| 43 | # ----- utils ----- |
112 | # ----- utils ----- |
| 44 | |
113 | |
| 45 | sub readFile { |
114 | sub readFile { |
| 46 | my $fileName = shift; |
115 | my $fileName = shift; |
| 47 | open INPUTFILE, "<", $fileName or die "Couldn't open environment file $fileName: $!"; |
116 | open INPUTFILE, "<", $fileName |
|
|
117 | or return; #die "Couldn't open environment file $fileName: $!"; |
| 48 | my $result = join "\n", <INPUTFILE>; |
118 | my $result = join "\n", <INPUTFILE>; |
| 49 | close INPUTFILE; |
119 | close INPUTFILE; |
| 50 | return $result; |
120 | return $result; |
| 51 | } |
121 | } |
| 52 | |
122 | |