Motivation
Configure is flexible, but often does unnecessary work, making it slow.
It requires re-checking everything every time it loads. This was fine when it was just a field validator, but over time many I/O intensive fuctions have been added, making it slow.
This provides a poor user experience. It also discourages users from visiting to check changes, which can result in service disruptions when errors aren't caught before changes are activated.
This grew out of some SMIME support work, but has evolved into a serious re-plumbing of configure.
Description and Documentation
Sounds like blue sky, but a prototype is running today.
This work addresses these issues as follows:
- It provides the ability for checkers to do work only on demand. Slow processes are not done every time.
- It provides the capability for real-time feedback when a field is changed. This catches errors before LocalSite.cfg is written and users are impacted.
- Features are controlled by the .spec file
- It exploits Tasks.Item12140 to provide default checkers for currently un-checked fields.
- It provides ETag support for resources, allowing browser caching of configure resources
- Is fully backward-compatible with existing checkers.
- There is additional work on robustness bundled with this, including changes to the resource server and backups of saved config files. I do not plan to break most of these out for a patch release.
Config file changes
There are two kinds of dynamic feedback.
- Check-on-click uses a button that is clicked by the user to initiate an action. The usual action is "Validate", which verifies that a field's contents are sensible and supported. A typical Validate action is to check file permissions. Other pre-defined actions are: "Test", which performs a functional test (e.g. sending email to a specified address) and "Fix", which attempts to repair a problem (e.g. changing file permissions or creating a directory).
- Check-on-change has no visible button, but feedback is provided when a field's contents change in the browser. A typical check-on-change field is a REGEX, where the feedback shows any REGEX compile errors.
Any given configuration item can have either, both, or none of these types of feedback. E.G. a check-on-change action that verifes syntax, and check-on click buttons for deep validation, repair and testing.
Items with dynamic feedback have a
FEEDBACK clause in their item's
options list.
FEEDBACK has the following syntax:
- FEEDBACK - Declares a button-based (check-on-click) item with the default label ("Validate")
- FEEDBACK=keyword- Declares an item with a standard action/label. Prefered form so that label changes only happen in the keyword table. Keywords are not case-sensitive.
- ON-CHANGE - Specifies a check-on-change item. These have no visible button, but initiate feedback when the field contents changes.
- IMMEDIATE - Synonym for ON-CHANGE
- AUTO - One more synonym for ON-CHANGE. Someday I'll pick just one. This one's short; the others descriptive.
- VALIDATE - Specifies a button with the default label for validating a field. ("Validate")
- TEST - Specifies a button with the default label for a functional test ("Test")
- FIX - Specifies a button with the default label for repairing a problem with a field. ("Repair")
- FEEDBACK="string" - Specifies a button with a custom label. Best to use a standard keyword where possible, add if necessary. Either single or double quotes are accepted, but must match. "\" quoting supported. Strings matching /^~/ are reserved for internal use.
- FEEBACK can have optional attribute clauses, specified as ;name, name:number or ;name="string". These immeditately follow the main clause, and are used to customize the feedback buttons.
- button= _n__ - Use _n as the logical button number (over-riding the default left-to-right default). Allows repositioning in .spec without changing code.
- class="string" - Add a custom CSS class (or space separated-list) to the <button> item - e.g. if a custom color or positioning is required.
- col=n - Use a table layout for buttons, place this one in column n(origin: 1).
- If no FEEDBACK clause specifies a column, buttons display in rows of 3 (no table).
- If any FEEDBACK clause specfies a column, a table is used. By default, columns increment for each FEEDBACK clause.
- If col is less (left) of default position, a new row is started.
- If col is greater (right) of default position, empty cells are inserted.
- html - Enables HTML in the label string (otherwise, it's escaped)
- span=n - In table layout, this button spans n columns. (html colspan)
- title="string" - Add a title (hover text) to the button. Use this to indicate function, warnings.
- value="string" - Provide a custom value (sent to the server on POST) for the button. Normally it's the button label.
- wait="string" - Use string instead of the default "Working..." message when this button is clicked. Use sparingly - intended to alert of very long potential wait times.
Butons are displayed under the configuration item name, left to right in the order they appear in the
.spec
file. There is no practical limit on how many buttons a configuration item can have; e.g. one each for
Validate and
Fix. If necessary, buttons will be displayed in multiple rows.
If a given configuration item has multiple
check-on-change buttons defined, all will be triggered by a field change.
Other config file changes:
Changes for Generic checkers
Configure now supports 'generic' checkers. These can support most items for a Type, usually without a line of perl code being written. To parameterize these checkers, the CHECK attribute is used in the options field.
The syntax is
CHECK="option option:val option:val,val", where checker options are separated by spaces, an optional value string is indicated by colon, and option values are delimited by commas (unless the value is a quoted string, in which case there's just one value, and \ must be \'d due to the nested strings). This allows, for example, the default
PATH checker support all the common checking options without individual checkers.
{ToolsDir}
looks like
Check="guess:tools perms:r"
, which tells it to guess the directory name (if it's undefined) using the installation root/tools as its guess, to do a read permissions check on the tree, and not to filter any files.
Checkers have access to a common parser for this format, and also gain an interface to getting/setting their item's value - eliminating the evals formerly required (and rarely error-checked) in generic checkers. See Checker.pm, Checkers/PATH.pm and Types/PATH.pm for details and a worked-out example. Checkers/URL.pm is another interesting resource.
Changes for Non-standard items
Certain plugins need more control over the presentation of their controls, without giving up the ability to leverage the existing infrastructure.
LABEL="string" allows an alternative to the {Key}{s} label for items in the settings table.
string can be any valid HTML for <td> context. An empty string allows only FEEDBACK buttons to be shown; specify this with the
NOLABEL keyword. (This is different from specifying neither, which provides the traditional {Key}{s} label.
CHANGE="string" allows javascript to be introduced into the
onChange flow (before feedback is initiated). To be used sparingly, but it's useful to provide immediate changes to the GUI presentation when tightly-coupled items change. e.g. clearing the value of a text field when a checkbox is unchecked, or setting a default when it is. The code executes in an anonymous function, with the changed element passed as
this. In some cases, other code executes first, but
CHANGE code can safely return from the middle. The return value is currently ignored - but to be extra safe, return true. If you need more than a line or two, do a function call (to
scripts.js) instead. The string is subject to my .spec continuation and quoting/escaping rules.
Configuration audit support
To allow meaningful subsets of checks to be simultaneously, and without refreshing the main page, the Audit tab has arrived. Every configuration item created from a .spec file is assigned a default
audit group, called
PARS:0.
Additional audit groups may be associated with an item in the .spec file, using the
AUDIT="G:b" syntax. (The
PARS:0 group can not be removed.) Dynamic items (created by plugins or configure itself), use an API to assign audit groups to those items.
The audit function is a special checker, subclassed from AUDIT, which has special access to configure's data structures and APIs. When an audit function is run, it inspects all configuration items' audit groups. The following magic happens:
- The audit function inspects it's list of audit groups, and identifies those that match the FEEDBACK button number that was pressed. (These are specified in the AUDIT.spec file, or one of its friends.)
- The audit function matches those group names to the audit group assigments of every configuration item. If the name matches, that item's checker will be selected to be activated.
- The audit function instructs FEEDBACK to invoke the selected checkers. (Several hundred items are checked with the basic checks.) The checks are either "traditional" checker runs - check() is activated, or
provideFeedback()
runs (as though a check-on-change or check-on-click event occurred). The specific check is controlled by the G:b notation in the AUDIT tag. As usual, provideFeedback()
checkers can cause other checkers to run when they exit.
- When the last checker has run, the audit function collects the output and does something interesting with it.
The
G:b notation is
GroupName followed optionally by
:ButtonNumber.
GroupName is simply a tag that allows items and the audit function to form sets of related items for an audit run. Both items and auditors can (and do) belong to multiple audit groups. For
auditors, the
ButtonNumber indicates which FEEDBACK button activeated the audit. Thus, a single checker can audit different ite msets depending on which button invokes it. For
items, the
ButtonNumber indicates which FEEDBACK button is to be "pressed" by the auditor to check the item.
ButtonNumber 0 invokes the
check()
function; buttons 1-9 invoke
provideFeedback
(or
check()) with the corresponding buttons defined for the item in FEEDBACK clauses. By defining the audit groups carefully, we can arrange for testing at different levels of completness, cost, and/or risk.
- It is possible for a checker to be both an auditor and an audit function; this is a bit involved and generally not recommended. CGISetup is an existence proof.
For example, current definitions include:
- PARS every (spec file) item belongs to this group as PARS:0. This allows repeating all the basic checks that are done when configure starts.
- EPARS is opt-in for the item (n the spec file), and is defined as extended validation. This means tests that take a bit longer, are low risk, more complete. It should NOT include reading 5,000 disk files. EPARS is a superset of PARS.
- DIRS is defined for directory and directory path validation. This is where one can read 5,000 files without fear. (Well, without fear of me - system administrators may feel differently).
- is the CGI environment
- CORE is the Foswiki core
- EXTN is the Foswiki extensions/core combined dependencies audit.
- Other group names will be defined; these have auditors assigned today.
Note that the two button numbers are independent. Pushing Auditor button 3 may invoke several audit groups (say, PARS and EPARS). Each participating item may run in either or both groups, and with a different virtual button press for each one. Further description is beyond the scope of this footnote to the Feeback infrastructure - which is the plumbing that makes the auditor possible. The preliminary AUDIT.spec file has more documentation.
Type Changes
There are several new
Type option flags:
- s/S - HTML5 'spellcheck' - lowercase s sets false, capital true.
- U - Allows 'undefined' values for an item without complaint. For legacy compatibility - please don't create new items like this - it prevents reporting errors in a number of cases.
- E - Generates an 'enable' checkbox for (currently only text) items. Items with Enable are undef'd on input if enable is clear, and have some magical properties in the GUI. Also a legacy support item. Generates a cfg hash item with a trailing underscore in the last key, e.g.
{key}{s_}
containing the value of the checkbox.
Except for
s/S, these are not considered part of the stable Foswiki API, and are subject to change.
Types can specify a
defaultOptions( $id, $opts, $feedback, $check )
method. This allows a Type to provide defaults for the "option" string, thus permitting a Type to default to
FEEDBACK=auto, or provide default
CHECK options for a sub-class.
A
defaultOptions
method returns the updated options string, and should be careful not to over-ride options in the .spec file. It is called before any
check
or
provideFeedback
method, which is called before the Type's
prompt
method.
Thus, the Type can influence
check
, and
check
can update values emitted by
prompt.
$feedback
and
$check
are booleans indicating whether the respective clause is present for the item.
Types should
not do detailed manipulation of the option string.
Although the entire string is re-parsed, the general effect of appending options is of a logical
or with what is in the .spec file, although there are some subtlties that a sensible user should not encounter...
A typical example:
sub defaultOptions {
my $this = shift;
my ( $id, $opts, $feedback, $check ) = @_;
$opts .= ' FEEDBACK=AUTO' unless ($feedback);
return $opts;
}
Checker Changes
No checker needs to change, though some should. The
check()
entry point is unchanged, but may be called in response to a button or field change.
Checkers that are upgraded split the check function into two methods:
-
check()
provides the contents of the item's status field, typically a NOTE( "Please press the Validate button to verify this path"). It may also do quick checks.
- provideFeedback() is only called in response to a check-on-click or check-on-change event.
-
check()
is called in response to check-on-click or check-on-change event if a provideFeedback
method is not implmented by a checker.
Work can be divided among the two methods in any way that an item requires. However,
check() should be fast and light, with any heavy work defered to
provideFeedback().
provideFeedback()
receives the button number, while
check()
does not. So any checker that has more than one button must implement (or inherit) a
provideFeedback()
method.
provideFeedback()is called with the $valueObject reference, the number of the button that was pressed, and the value (label if not specifed in the .spec file) of that button. It returns
NOTE, WARN, or
ERROR strings just as
check()
does today. (The value is currently '~' for
check-on-change fields, but this is not guaranteed.) By default, buttons are numbered from 1 in the order they appear in the
.spec
file: left to right for each item. Button code 0 is reserved to mean "the
check()
method" in certain contexts. And all button numbers less than zero are reserved for internal functions.
provideFeedback()can modulate how much work it does based on which button was clicked:
check-on-change should be kept lightweight for maximum responsiveness;
check-on-click should take all the time it needs.
A typical (upgraded) checker would look like this:
The code in
purple is what is added to a current checker. The code in
blue is used for class checkers and/or checkers that support multiple buttons.
For a more complex upgraded checker, see the following extract from
Foswiki::Configure::Checkers::PATH
. This demonstrates how one can divide work between the
check
and
provideFeedback
methods, how to handle prompting, value expansion and value changes.
provideFeedback()is run with
%Foswiki::cfg containing all values shown in the GUI - including unsaved selections of other configuration items. Thus it is checking what
would be saved.
provideFeedback() can also influence which (if any) of the other items' checkers (this means
check(),
not provideFeedback()
) are run after it provides feedback. To do this, it returns an array, in which the second element contains:
- 0 - Do not run any other checkers (Typ. a file permissions checker doesn't interact with other settings)
- 1 - Run only checkers for other items that have changed since the last save. (Default).
- 2 - Run all checkers (except the one that just ran).
- an array reference - run
provideFeedback
on the items named in the array. Names can be suffixed with a button number (by default they run with the number of the triggering button.) E.g. [qw/{Happy}{Checker}4 {Sad}{Checker}0/] will run Foswiki::Configure::Checkers::Happy::Checker::provideFeedback
with button 4, while Foswiki::Configure::Checkers::Sad::Checker::check()
will run with no button at all. These will run in the order specified. If the referenced checkers also schedule additional checks, they happen after the penultimate item in the list. There is a limit to catch circular dependencies - but don't do that.
provideFeedback() normally delivers its output to the results area under its item's value. However, it can provide updates to any item(s), but using the special
FB_FOR method. $this->FB_FOR('{AnyKey}', $this->WARN("...")) can be the only return text, or can be included in text for the default window. Multiple
FB_FOR() calls can be included in a result. This enables a checker to update related items. To update a named item that is not in
%Foswiki::cfg
(e.g. items in the magical
{ConfigureGUI}
class), use
FB_GUI
provideFeedback() can also update the value of any input control on the GUI, for example when a checker computes (or guesses) a value. Similar to
FB_FOR, the
$this->FB_VALUE('{AnyKey}', value) method is embedded in the return text string. For items not in
%Foswiki::cfg
, use
FB_GUIVAL.
FB_MODAL provides control of modal dialogs. The
value argument(s) vary by type:
- For
select
items, specify the option value to be selected.
- For
select-multiple
items, specify a list of option values to be selected. The first one specified will be the selectedIndex
.
- For
checkbox
or radio
items, specify 1
, on
or true
to check/select.
- For text fields (
text
, textarea
, hidden
, password
), multiple values will be concatentated.
FB_ACTION delivers some miscellaneous commands to the client. Currently, it allows positioning a scrolled DIV and managing JS-sensitive content, but this will expand as new needs are uncovered. The definitive reference is scripts.js.
All the
FB_ methods produce command blocks that are executed by the client javascript in a feedback response. These blocks are ignored in the ouput used to build the initial page. All non-command text contained in ouput from a checker is coalesced (in order) into a single string that updates the results area. In a feedback response, this implicitly generates an results update command block for the item, which will be executed before any explicit commands generated by the checker in that stream. Commands are executed in order. The exact format of command blocks is internal to configure and subject to random change. Only the API provided by the
FB_ methods and
Foswiki::Configure::UI::parseCheckerText()
is stable. The
FB_ methods emit empty strings if called in a legacy environment.
Note: In early versions of the API,
FB_ methods could only be called from
provideFeedback()
methods. Thus, many checkers go to some trouble to have their
check() methods pass information to their
provideFeedback()
methods. This is no longer required, although it is important to remember that
FB_ methods do nothing when called in page build or legacy environments.
Everything is styled with CSS (not well, but it enables a real graphics person to modify it).
There is one text string added to the javascript; this can be intenationalized by including a
<hidden> input field to the form template.
Examples
- Configure currently checks all paths, scanning up to 5,000 files and/or directories of each path for permissions issues. This is now done on-demand.
- Broken REGEXs can crash or at best cause malfunctions. These are now checked before save.
- The webserver environment tab reads dozens of perl modules just to extract version numbers - and even loads Foswiki.pm and all its dependencies, again for every instantiation of configure. This is now done on-demand.
- I18n support
An extract of the
PATH checker mentioned earlier follows. Some of the diagnostic code and path-specific initialization code has been removed and messages shortened to show the new flows. Note how
check()
does only lightweight checking, and
provideFeedback()
calls
check()
to redo those checks each time it is activated. As a generic checker, PATH is a good example of the new interfaces. See the actual module for precise information. Also note how a
guessed (or otherwise modified) value is sent to the web-browser.
sub check {
my $this = shift;
my $valobj = shift;
my $keys = $valobj->getKeys();
my $e = '';
$e = $this->NOTE("Please click the Validate button to test this path")
unless ( $this->{FeedbackProvided} || !$this->{item}->feedback );
my @optionList = $this->parseOptions(); # This gets and parses the list of CHECK=" " options.
my $guessed;
foreach my $opts (@optionList) {
my $guess = $opts->{guess};
if ($guess) {
$guessed = 1;
my ( $dir, $rootkey, $silent ) = @$guess;
return $this->ERROR(".spec error: no guess") unless ($dir);
$e .= $this->guessDirectory( $keys, $rootkey, $dir, $silent );
}
}
my $value = $this->getItemCurrentValue();
if ( $guessed && $e =~ /I guessed this/ ) {
$this->{GuessedValue} = $value;
}
$e .= $this->warnAboutWindowsBackSlashes($value);
$e = $this->showExpandedValue($value) . $e
unless ( $this->{GuessedValue} );
if ( !$this->{item}->feedback && !$this->{FeedbackProvided} ) {
# There is no feedback configured for this item, so do any
# specified tests in the checker (not a good thing).
$e .= $this->provideFeedback( $valobj, 0, 'No Feedback' );
}
return $e;
}
sub provideFeedback {
my $this = shift;
my ( $valobj, $button, $label ) = @_;
$this->{FeedbackProvided} = 1;
# Normally, we call check first, but not if called by check.
my $e = $button ? $this->check($valobj) : '';
delete $this->{FeedbackProvided};
my $e2 = '';
my $keys = $valobj->getKeys();
my @optionList = $this->parseOptions();
foreach my $opts (@optionList) { # Perform each CHECK= operation in the order specified
my $perms = $opts->{perms};
my $filter = $opts->{filter}[0];
if ($perms) {
my $checkPerms = $perms->[0];
$checkPerms =~ s/d//g if ( $Foswiki::cfg{OS} eq 'WINDOWS' );
$e2 .=
$this->checkTreePerms( $this->getCfg($keys), $checkPerms,
$filter );
}
}
if ( $this->{filecount} >= $Foswiki::cfg{PathCheckLimit} ) {
$e .= $this->NOTE( "File checking limit $Foswiki::cfg{PathCheckLimit} reached, checking stopped" );
} else {
$e .= $this->NOTE("File count: $this->{filecount} ");
}
# More diagnostics removed for clarity
if ( $this->{GuessedValue} ) {
$e .= $this->FB_VALUE( $keys, ( delete $this->{GuessedValue} || '' ) );
}
return wantarray ? ( $e, 0 ) : $e;
}
}
There are some new sorts of checker-like objects that support modal objects and auditing.
Impact
This work touches several dozen files (so far), though most of the changes are small enabling hooks. Configure proper has been seriously reorganized due to the authentication changes required.
Upgraded checkers that fully exploit the potential of this work will not function as expected with old Foswiki releases unless considerable extra effort is made in the checkers. I recommend that such products discontinue support for older Foswiki releases, or ship their previous checker. I don't see this as a major drawback, but it should be pointed-out.
However, old checkers do not need any changes to function; the upgrade path is smooth.
This work exploits and extends the default checker mechanism that I introduced in item 12140 for the Tasks Framework. A Type can provide a makeChecker method that produces a checker for any item that doesn't have its own. It can also have a generic checker (named the same as the Type, but in the Foswiki::Configure::Checkers:: namespace), which will be invoked for all items of that type that don't otherwise have one. This allows greatly increasing checker coverage without writing any perl code. (Type checkers need to assume reasonable defaults for items without guidance in the Foswiki or extension .spec file.)
It is now possible to parameterize these default checkers in the .spec file, eliminating one more reason for configure to load almost-null modules to satisfy the class system, and making it possible to produce more checkers without writing a line of perl code.
An un-validated field is a support issue in-waiting.
[Syntax moved up to be with other .spec file changes]
The GUI is modified to support this. Besides the feedback buttons, the checker output is contained in a (potentially) scrolling DIV under each item in order to make a reasonable overview of each tab visible. If a div gains scroll-bars, it also gains an expander button that will open the div to full size. The info/help text has been moved to above the item (where it has been historically). The current placement under the errors moves it too far from the entry field, and in any case, we hope that the help is read BEFORE entering values... Other impacts of non completely rebuilding the screen on each change ripple thorugh the client javascript.
Implementation
--
Contributors: TimotheLitt - 08 Oct 2012
Discussion
Here is an early screen shot from the prototype. It shows the GUI, the config spec, and what happens to a checker.
Also, what happened after I clicked one of the buttions on the same screen. You can have multiple buttons
per item (e.g. for quick check, slow check & fix).
I'm not much of a graphics person, but I added CSS support so those who are can fool with the buttons. Here's a crude mockup with three buttons on an element:
When a
check-on-click or
check-on-change update occurs, the headline total errors, warnings, and the summary icons on the tabs are all updates, as well as the number of unsaved items.
In a private communication, Sven urged acceptance of this proposal.
--
TimotheLitt - 13 Oct 2012
We could add an "updated" state indicator to the errors and warnings. In any case should be made clear that Save is required to make changes permanent.
--
ArthurClemens - 13 Oct 2012
The Save required idea is a good one, thanks! Implemented, see below.
The errors and warnings is a harder problem, as it requires saving state that's not easy to get to. I'll do it if I think of a clever approach, but I don't want to do serious
re-plumbing - at least not in pass 1.
--
TimotheLitt - 13 Oct 2012
Added description of FB_VALUE() method. Tried to undo formatting breakage.
--
TimotheLitt - 16 Oct 2012
Better copy: "You've made 11 changes. Continue and Save to make them permanent."
Not in the top bar of course.
--
ArthurClemens - 16 Oct 2012
This proposal looks great! FYI I had raised a task that is very related to this proposal, for sanity checking input fields that are fragments of perl:
Tasks.Item11392
[TL]: There is now a Type-default checker for PERL items that resolves that issue.
--
KipLubliner - 20 Oct 2012
Documentation updates and more complex example.
--
TimotheLitt - 24 Oct 2012
One of the things I did as part of this work is improve the resource server and performance generally.
configure now generates
ETag headers and honors
If-none-match headers (generating 304 Not modified) as appropriate.
It also allows caching of static resources and some pseudo-static resources.
An aspect of this is that
configure's resource server now attempts to honor the
Accept-Encoding: gzip http header from the browser.
This allows configure to compress it's output - it's a big performance win, as the size of the transfers drops by 80%.
configure will use Content-Encoding: gzip when:
- The browser's Accept-Encoding header includes gzip and
- The content will benefit from compression and
- *gzip* content is available or can be created.
Two aspects of this merit some documentation and a little discussion.
- Dynamic output (like the configure screens, and resources that undergo %INCLUDE{}% and/or %VARIABLE% subsititution can only be gzip'd if IO::Compress::Gzip is installed. This is currently a run-time check, but I'd like to add IO::Compress::Gzip to DEPENDENCIES.
- Static/apparently static content is handled differently. The algorithm is:
- Unless the content has MIME type text/*, serve the requested resource unmodified. Skip the following steps.
- Look for resource .gz. If it exists, it is served in place of the resource. The content is not inspected; no variable substition will be done.
- Otherwise, the file is parsed and any substitution is done (and noted).
- If any substitution occurred, the file is considered "dynamic", and the ETag will include an MD5 of the content. (Most dynamic content is simply the site-specific URI, so it can be re-used.)
- If the result can be gzip-encoded (and meets a minimum size threshold), it is. If the the file is dynamic, everyone is happy. If not, we just wasted a lot of computes. There should have been a .gz file. (There's an optional log message for this.)
This all sounds like goodness, so what's to discuss?
- How do we get the static .gz files?
- This is a bit tricky, in that we really want the files to exist if the content is static, but they must not exist if the content is dynamic.
- I provided a script (make_gz) that inspects the content and creates or deletes the .gz for all the files (or selected files) in a directory.
- For now, I checked in the results...
- But: the packaging/build/pseudo-install processes need to learn about this, and the MANIFEST automagically adjusted.
- There has been some (private) confusion about the relationship of gzip encoding to minify/debug versions of files. These are two (almost) separate issues.
- configure serves the resource it's asked for. Whether the content is minified or not is transparent to configure. So, as today, one edits the template or renames the file to switch.
- But: if the content is static (like jQuery, but unlike some CSS with parameterized URIs), you need to rename both the resource and the resource.gz files.
--
TimotheLitt - 03 Nov 2012
Trying to sum-up the discussion we had last night with Timothe:
- The static .gz files should simply use the existing MANIFEST mechanism, that is: any file which can be compressed should have a corresponding .gz entry in the MANIFEST, and both pseudo-install and BuildContrib will take care of it
- It would be nice to add a checker to both of those for the content of what's being gzip'ed. Basically, that both pseudo-install and BuildContrib would do the same check as Timothe's
make_gz
(checking for %INCLUDE% and %VarXXX% mostly)
- Configure could have an extra on-demande checker, which checks that the .gz is more recent than the uncompressed version, otherwise prints an error, and offers the possibility to compress it back.
- NEVER commit the .gz files. This is a reminder, nothing against Timothe's excellent work. The idea is double:
- gzip'ed versions are hard to keep in sync, and people need to remember to compress and commit the compressed version. This has led to troubles in the past, which is why nowadays, BuildContrib and pseudo-install takes care of this
- Compressed versions do not belong in version control systems, as it is impossible to diff them. As we already have the uncompress versions, we shouldn't need it
- More importantly (that is the second point, the previous one was just rhetorics), BuildContrib and pseudo-install will most likely OVERWRITE any gzip'ed version on-the-fly, which could lead to even more confusion.
All in all, very nicely done, and a great addition. I would personally ask to have an AJAX call upon every tab switching, so that the amount of pending changes is regularly updated. But that's a minor detail.
- Minor detail that has already been addressed. Great work guys, Arthur and Timothe!
--
OlivierRaginel - 04 Nov 2012
Updated available update functions with another magical function. Noted that (despite my early worries), the GUI now updates the alert summary lines and icons in (near) real-time. The updates are a little more fragile than I'd like, but they all happen in the client, so if the screen layout is changed incompatibly, the same person is responsible for updating the javascript.
--
TimotheLitt - 07 Nov 2012
Update documentation with additional spec file syntax and related information addressing several user experience bugs and wishes.
--
TimotheLitt - 15 Nov 2012
More documentation.
--
TimotheLitt - 07 Dec 2012
Significantly simplified in the 1.2 implementation - see
HowToWriteASpecFile
--
CrawfordCurrie - 28 Aug 2014