Skip to content

Commit 671cf98

Browse files
committed
Update the Rserve interface.
The primary change in this pull request is how the `rserve_get_file` method in the `RserveClient.pl` macro works. Previously it accepted a second optional local filename parameter. That parameter is no longer honored. If it is given it is ignored. I don't think that there are any problems in the OPL or Contrib that pass that argument, but even if they do it will still work. The filename just won't be the chosen one. The filename should not be a problem author choice. Furthermore, previously the method fell back to using the Rserve remote filename. That is also no longer used. Instead, the `getUniqueName` method is used that creates a filename that is dependent on the problem seed, psvn, problem UUID, etc. That means that the filename will not change each time that the problem is loaded. Unfortunately, the file is still downloaded every time the problem is loaded and overwrites the existing file (with the same contents), and that can't really be fixed without a major revision of the Rserve setup. Note that all changes in this pull request are backwards compatible, and all existing problems will continue to function correctly. However, the courses html temporary directory will no longer be filled with files that are only used once before another copy of the file is created by another name the next time the problem is rendered (due to a preview, answer submission, etc.). In addition there are two new methods added that are more convenient than the previous methods for generating R images or csv files. Those are the `rserve_data_url` and `rserve_plot` methods. These handle the details of creating these things in a much nicer way. See the added POD and examples in the POD for the usage and comparison to the old approaches. The macro code and POD are also rather heavily clean up. The `Rserve.pm` module is also cleaned up. The `_inherits` method is removed from the `Rserve.pm` module, because contrary to the comments the `inherits` method of the `$rserve` object does work in the safe compartment, and so the `_inherits` method is not needed. There are a couple of other related changes. First, the `htmlLink` method now has a new calling convention for adding additional attributes to the generated `a` tag. Previously these were added by passing a single string argument. For example, `htmlLink($url, 'dataset', 'download="dataset.csv"')`. That will still work, but passing the attributes in that way should be considered deprecated. Instead the attributes should be passed as additional attribute/value pairs after the url and link text. For example, `htmlLink($url, 'dataset', download => 'dataset.csv')`. Of course, this is useful for Rserve csv files to specify the filename that will be offered when the user clicks to download a csv file. Particularly since the default will now be something like `ed721060-00b2-37fb-87c4-e3eb36c7ced2___5643740d-c34f-37c8-afc5-c2369fec26e1.csv`. Previously it would have been something like `file1231asdfasdf.csv`. The new alias is worse, but that wasn't good either.
1 parent 6351166 commit 671cf98

File tree

4 files changed

+253
-151
lines changed

4 files changed

+253
-151
lines changed

conf/pg_config.dist.yml

+6
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,12 @@ specialPGEnvironmentVars:
139139
# Size in pixels of dynamically-generated images, i.e. graphs.
140140
onTheFlyImageSize: 400
141141

142+
# To enable Rserve (the R statistical server), uncomment the following two
143+
# lines. The R server needs to be installed and running in order for this to
144+
# work. See http://webwork.maa.org/wiki/R_in_WeBWorK for more info.
145+
#Rserve:
146+
# host: localhost
147+
142148
# Locations of CAPA resources. (Only necessary if you need to use converted CAPA problems.)
143149
CAPA_Tools: $Contrib_dir/CAPA/macros/CAPA_Tools/
144150
CAPA_MCTools: $Contrib_dir/Contrib/CAPA/macros/CAPA_MCTools/

lib/Rserve.pm

+18-37
Original file line numberDiff line numberDiff line change
@@ -11,53 +11,34 @@ my $rserve_loaded = eval {
1111
sub access {
1212
die 'Statistics::R::IO::Rserve could not be loaded. Have you installed the module?'
1313
unless $rserve_loaded;
14-
15-
Statistics::R::IO::Rserve->new(@_);
14+
return Statistics::R::IO::Rserve->new(@_);
1615
}
1716

18-
## Evaluates an R expression guarding it inside an R `try` function
19-
##
20-
## Returns the result as a REXP if no exceptions were raised, or
21-
## `die`s with the text of the exception message.
17+
# Evaluates an R expression guarding it inside an R `try` function
18+
#
19+
# Returns the result as a REXP if no exceptions were raised, or
20+
# `die`s with the text of the exception message.
2221
sub try_eval {
2322
my ($rserve, $query) = @_;
2423

25-
my $result = $rserve->eval("try({ $query }, silent=TRUE)");
26-
die $result->to_pl->[0] if _inherits($result, 'try-error');
27-
# die $result->to_pl->[0] if $result->inherits('try-error');
24+
my $result = $rserve->eval("try({ $query }, silent = TRUE)");
25+
die $result->to_pl->[0] if $result->inherits('try-error');
2826

29-
$result;
27+
return $result;
3028
}
3129

32-
## Returns a REXP's Perl representation, dereferencing it if it's an
33-
## array reference
34-
##
35-
## `REXP::to_pl` returns a string scalar for Symbol, undef for Null,
36-
## and an array reference to contents for all vector types. This
37-
## function is a utility wrapper to make it easy to assign a Vector's
38-
## representation to an array variable, while still working sensibly
39-
## for non-arrays.
30+
# Returns a REXP's Perl representation, dereferencing it if it's an
31+
# array reference
32+
#
33+
# `REXP::to_pl` returns a string scalar for Symbol, undef for Null,
34+
# and an array reference to contents for all vector types. This
35+
# function is a utility wrapper to make it easy to assign a Vector's
36+
# representation to an array variable, while still working sensibly
37+
# for non-arrays.
4038
sub unref_rexp {
41-
my $rexp = shift;
42-
39+
my $rexp = shift;
4340
my $value = $rexp->to_pl;
44-
if (ref($value) eq ref([])) {
45-
@{$value};
46-
} else {
47-
$value;
48-
}
49-
}
50-
51-
## Reimplements method C<inherits> of class L<Statistics::R::REXP>
52-
## until I figure out why calling it directly doesn't work in the safe
53-
## compartment
54-
sub _inherits {
55-
my ($rexp, $class) = @_;
56-
57-
my $attributes = $rexp->attributes;
58-
return unless $attributes && $attributes->{'class'};
59-
60-
grep {/^$class$/} @{ $attributes->{'class'}->to_pl };
41+
return ref($value) eq 'ARRAY' ? @$value : $value;
6142
}
6243

6344
1;

macros/core/PGbasicmacros.pl

+22-14
Original file line numberDiff line numberDiff line change
@@ -2231,11 +2231,6 @@ =head2 Formatting macros
22312231
# The function should be called either with value specified (immediate reference) or
22322232
# with url specified in which case the revealed text is taken from the URL $url.
22332233
# The $display_text is always visible and is clicked to see the contents of the knowl.
2234-
htmlLink($url, $text)
2235-
# Places a reference to the URL with the specified text in the problem.
2236-
# A common usage is \{ htmlLink(alias('prob1_help.html') \}, 'for help')
2237-
# where alias finds the full address of the prob1_help.html file in the same directory
2238-
# as the problem file
22392234
iframe($url, height=>'', width=>'', id=>'', name=>'' )
22402235
# insert the web page referenced by $url in a space defined by height and width
22412236
# if the webpage contains a form then this must be inserted between
@@ -2362,18 +2357,31 @@ sub OL {
23622357
);
23632358
}
23642359

2360+
=head2 htmlLink
2361+
2362+
Usage: C<htmlLink($url, $text, @attributes)>
2363+
2364+
Places an HTML link to C<$url> with the specified C<$text> in the problem. The
2365+
C<@attributes> are optional. They should be provided as attribute/value pairs,
2366+
but a single text string argument can be given (although calling C<htmlLink> in
2367+
that way is deprecated and should not be done in new problems). For example,
2368+
2369+
BEGIN_PGML
2370+
Download the [@ htmlLink($url, 'dataset', download => 'dataset.csv') @]*
2371+
for this problem.
2372+
2373+
=cut
2374+
23652375
sub htmlLink {
2366-
my $url = shift;
2367-
my $text = shift;
2368-
my $options = shift;
2369-
my $sanitized_url = $url;
2370-
$sanitized_url =~ s/&/&amp;/g;
2371-
$options = "" unless defined($options);
2372-
return "$BBOLD [ the link to '$text' is broken ] $EBOLD" unless defined($url) and $url;
2376+
my ($url, $text, @options) = @_;
2377+
return "$BBOLD [ the link to '$text' is broken ] $EBOLD" unless $url;
2378+
my $attributes = @options == 1 ? $options[0] : {@options};
23732379
MODES(
23742380
TeX => "{\\bf \\underline{$text}}",
2375-
HTML => "<A HREF=\"$url\" $options>$text</A>",
2376-
PTX => "<url href=\"$sanitized_url\">$text</url>",
2381+
HTML => ref($attributes) eq 'HASH'
2382+
? tag('a', href => $url, %$attributes, $text)
2383+
: qq{<a href="$url" $attributes>$text</a>},
2384+
PTX => '<url href="' . ($url =~ s/&/&amp;/g) . qq{">$text</url>},
23772385
);
23782386
}
23792387

0 commit comments

Comments
 (0)