Package: gf.rejuice
gf.rejuice`gf.rejuice` provides additional tools for developers to use `Juicer`
for the compression of Javascript and CSS resources,
in the
context of python web applications that run via WSGI.Q:Is this for me, shall I use it?A:If you develop javascript with some python based (WSGI) application, and you already
use Juicer, or consider using some method for compressing your resources, then you may
findgf.rejuiceuseful.This is a package under active development.Q:Is is safe to use it?A1:No. Although it is well tested, it may contain bugs, or may not support your use
case. Things can also change substantially, in a following version.A2:Yes, it is safe, because you actually only use it for development. It is not
even installed on the production website. So, the risk is minimal.Objectivesgf.rejuiceattempts to provide additional tools for developers to useJuicerin the
context of python web applications that run via WSGI.Juiceris a CSS and JavaScript packaging tool, that provides a method to compress
(minify) and concatenate the
resources for a website or a software package.
(Juicer supports the widely used and stableYUI Compressor.)
This compression happens manually, offline.
Then, if this resource is used in a website page, the
page can use the compressed resources directly.It is, however, very difficult or close to impossible to debug and develop websites with fully
compressed resources.
As a developer, my every day work routine needs an easy way to switch to a “development mode”,
where I am able to access the original set of uncompressed, undeveloped resources.One possible solution for this would be to provide two sets of resources from the website pages: one for
production that contains the compressed resources, and one for development. This can
be achieved in several ways, for example, by providing two sets of resources from the html headers
conditionally, or use some kind of registry that supports a Development / Production mode switch.
The problem with these approaches is that they almost always require
changes in the software and possibly extra administration, redundancy, which can be
especially painful if the project has many resources.As an alternate solution, to avoid a dual set of resources,
some tool can be used that supports the compression and
minification of the original sources on the fly. This however has the disadvantage that
due to the fact that even the most robust compression methods are prone to errors,
it is more difficult to verify the validity of the produced resources, than in offline
mode.gf.rejuiceattempts to provide a simple way to aid this process.
In comparision with existing tools, that support the compression and
minification of the original sources on the fly,gf.rejuicesupports a workflow the other way around.
It takes the minified resources as the reference, and provides access to the uncompressed
resources for development.It does this by providing a way to transparently switch a site
that contains and refers the compressed (minified) resources only, into development mode.
It does this without the need to change the original software and templates.
In addition, it also provides a way to automate the process of compressing or recompressing
the resources for production, in case this becomes necessary due to some changes
that have been made to the sources.Licensegf.rejuiceis dual-licensed by the GPLv2 and BSD licenses. You have to choose
any one or both of these licenses for the redistribution of this software.gf.rejuicedoes not contain or redistribute Juicer itself, Juicer still needs to be
installed separately, but even this is not necessary for each use case.InstallationYou need to install thegf.rejuicepackage to be importable from your project.
If you use buildout, this can be done by addinggf.rejuiceto your egg section:eggs =
    ....
    gf.rejuiceIn addition, you need to configure apaster.inifile that you will use for development.To use the current newest development version, you can download the package from its
source repository and install it
package manually. Alternately, you can use a recipe likegf.recipe.bzror similar from your buildout
to install the software directly from the source repository:[src]
    recipe = gf.recipe.bzr
    urls =
        bzr+ssh://gfpublic@greenfinity.hu/home/gfpublic/gf.rejuice/trunk gf.rejuice(To use gf.recipe.bzr, you need to have Bazaar installed on your computer.)Simple configurationTo configuregf.rejuice, you need to provide a development configuration for paster.
This can be done by copying your original ini file to a second ini file.
Then, add the necessary configuration
to the development (second) ini.For example, your application had a paster configuration filemyapp.ini, and you
create a secondmyapp-devel.inifrom it.Following this, you can use paster with the original, unchanged ini filemyapp.inito run the
application in production mode, and themyapp-devel.inifile
will be used to run the application in development mode.The following describes the parts to be added tomyapp-devel.inito produce a simple
working configuration:[filter:rejuice]
use = egg:gf.rejuice#develjuice
url_prefix = /static
filepath  = egg:my.package/views/static

# Resource composition:
default.min.js       = default.js
min/default.min.css  = custom/default.css

[pipeline:main]
pipeline =
    ...
    rejuice
    main_appThe WSGI filter sectionIn the above example, the[filter:rejuice]section defines the wsgi middleware that does the transformation
from production resource urls produced by the themain_app, to the corresponding
development resource urls. Which application is generating the page to be transformed,
or in which particular method this happens,
is ambivalent for this process.Theurl_prefixandfilepathparameters are needed to specifywherethe resources reside,
following that a resource list specifieswhatresources the WSGI filter should consider.url_prefix, filepath: specifying the static tree of resourcesTheurl_prefixandfilepathparameters must always stand together with the actual list of resources.
Together they specify a static tree in your application.url_prefixspecify the prefix of the url where the root of this static tree appears in the
application’s url space. The setting:url_prefix = /staticspecifies that thehttp://127.0.0.1:6543/staticurl is the root of the static tree that your application
publishes.filepathspecifies where the same static tree can be found in the filesystem:filepath = views/staticIn this example, the path is constructed relatively to the directory of themyapp-devel.iniconfiguration
file that holds the declaration.Note that thegf.rejuicemiddleware does not actually publish this static tree, it still is the
responsibility of your application. The parameters are needed for url rewrite.Specifying resource compositionWith the rest of the section content, you can specify a list of javascript or css resources here.
Anything ending with.jsor.csswill be considered to be a resource:default.min.js       = default.js
min/default.min.css  = custom/default.cssEach line contains two values here. The value on the left of the equal sign specifies the
compressed, minified resource (the result of compression). The right value specified the
original, uncompressed source (the origin of compression).
All file paths are applied relatively from the root of the static
tree, specified byurl_prefixandfilepath.Later we will see that it is also possible to produce one resource from a list of resources,
which, in that case will be compressed and concatenated to a single resource. The configuration
allows a list of resources in multiple lines:default.min.js =
    dependency.js
    ...
    default.jsResult of the configurationLet’s assume that you have the following resources specified from the head section of your html page:<script src="http://127.0.0.1:6543/static/default.min.js" type="text/javascript"></script>
<link href="http://127.0.0.1:6543/static/min/default.min.css" type="text/css" rel="stylesheet">The middleware will transform this to a website that loads the original sources:<script src="http://127.0.0.1:6543/static/default.js" type="text/javascript"></script>
<link href="http://127.0.0.1:6543/static/custom/default.css" type="text/css" rel="stylesheet">Juicer also allows to specify dependencies for the resources. The dependent resources
may also contain further dependencies. For further explanation, please read the
documentation ofjuicer.default.js:/* @depends a/source_a.js
   @depends b/source_b.js
*/custom/default.css:@import ../a/style_a.css
@import ../b/style_b.cssThe middleware will add the dependent javascript resources to the html headers. For css
this is not needed, as@importdescribes the inclusion dependencies in a native way for the browser.
The end result will be a html that contains the full set of original resources:<script src="http://127.0.0.1:6543/static/a/source_a.js" type="text/javascript"></script>
<script src="http://127.0.0.1:6543/static/b/source_b.js" type="text/javascript"></script>
<script src="http://127.0.0.1:6543/static/default.js" type="text/javascript"></script>
<link href="http://127.0.0.1:6543/static/custom/default.css" type="text/css" rel="stylesheet">For more complex examples on how to specify dependencies forjuicer, you can look
at the sources of theBottlecap UI, that provides the dependencies to compress thejquery-uiframework.Two ways to produce the same resultRather than specifying the dependencies in the style of Juicer,
by using@importfor css and@dependsfor js, the same results can be achieved
directly from the configuration as well:default.min.js =
    a/source_a.js
    b/source_b.js
    default.js
min/default.min.css =
    a/style_a.css
    b/style_b.css
    custom/default.cssThis configuration could replace and is equivalent with the usage of@importand@dependsin the above example.Which one of the two ways are better to use, depends on use case and personal taste.CompressionAlthough it is quite easy to use Juicer directly from the command line to produce the
compressed resources,gf.rejuiceprovides an automation for this.To use Juicer, you must have Juicer and its dependencies already installed. The
documentation of Juicer describes this simple process. This installation
(including java and ruby) does not need to be present for the middleware to work,
only for carrying out the actual compression.If you use buildout, you can enter the following from the command line:bin/rejuice paster_devel.ini filter:rejuiceWith the example ini file described earlier, this will produce or reproduce bothdefault.min.jsanddefault.min.cssfrom their original sources.filter:rejuicehere refers to
the juicer definition section from the ini file. Any extensions referred from this
section are also processed.An extension section can also be specified directly:bin/rejuice another_config_file.ini juice_resourcesIf you do not use buildout, you can also run therejuice_script.pyfile directly:python path/to/package/gf/rejuice/rejuice_script.py paster_devel.ini juice_resourcesAdvanced configurationIn this section we explain the usage of all parameters. They belong to two groups: the first group
specifies which urls the middleware should consider for transformation at all, the second
group gives control over selecting the resources that should be considered, by specifying a static
tree and by specifying the resource composition itself.Affecting which urls the middleware should considerThe task of the middleware is to transform only resources that are on the same server as where the
page is. So it needs to decide which resources are local and which not.By default, the middleware considers those resources as local, whose domain matches the domain
of the currently served page. To do this, nothing needs to be specified.For example, if your application is served from this url:http://127.0.0.1:6543/Then the following resources will be considered local, if they appear in the page header:http://127.0.0.1:6543/default.js
http://127.0.0.1:6543/static/default.css
/any/absolute/path/my.js
any/relative/path/my.cssBut the following resources will be considered remote, and thus ignored:http://github.com/jquery/qunit/raw/master/qunit/qunit.js
http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.2/themes/smoothness/jquery-ui.css
http://localhost:6543/static/default.cssThis is adequate for most web applications, that generate resource names in a way that they
use the base domain of the currently served page. If this is not a case (note thatlocalhostwould not
be accepted as local if the page is served on127.0.0.1), or the use case needs finer control, there
are two parameters that make this possible.base_urls: specifying the url host to considerThebase_urlsparameter specifies the domain and prefix url for the static tree, in which
both the production and development resources must be available and accessible from the web.
Only the resources that match this url, will be transformed.It is also possible to specify a list of urls for this, in which case any of the urls
will be considered for matching:base_urls =
    http://127.0.0.1:6543/
    http://localhost:6543/
    http://foo.bar/One specific case when this is needed, if the local resources may have a different domain than
the base url of the page in which they are referred from. In this case you simply list all the
possible urls you may want to use during development. Specifying more does not do any harm,
besides making the designated resources local.allow_request_url: disabling to consider the url host of the current requestallow_request_urlis by default True, meaning thatboththe domains frombase_urlsandthe domain of the current request url designate the locality of resources.It is possible to specifyallow_request_urlas False, in which case, the use of the request
url is prohibited, and only the declarationbase_urlswill be considered.This would be useful, if your portal is not at the root of domain, for example, it is served from the following
root url:http://127.0.0.1:6543/myportal/...In this case you would use the following specification, which will causeurl_prefixto be appliedafterthe prefix you specify frombase_urls:allow_request_url = false
base_urls =
    http://127.0.0.1:6543/myportal
    http://localhost:6543/myportal
    http://foo.bar/myportalSpecifyingallow_request_url = false, without also specifyingbase_urlsto a sensible value, does not make sense,
although it will not yield an error. The middleware will simply do nothing,
not being able to identify any url as local.Specifying the static tree containing the resourcesEach resource thatgf.rejuicehandles is part of a static tree.
Both the development, and the production resources
are in some static tree. This tree resides somewhere on the filesystem, and the application also
publishes this tree somewhere in its url space. The following parameters make it possible to define
this tree. Bothurl_prefixandfilepathneed to be defined together.url_prefix: specifying the url prefix of the static tree rootTheurl_prefixparameter is an url path segment that
defines where the tree root is located in the application’s url space:url_prefix = /staticThe above example defines that the static tree url is:http://127.0.0.1:6543/staticThe/in the beginning (or end) of the prefix is optional, that is, the following parameters
have an equivalent meaning:url_prefix = /static/pathurl_prefix = static/pathurl_prefix = /static/path/filepath: specifying the static tree root in the filesystemThe application uses the`filepath` parameter to locate the static tree on
the filesystem. This must be the same static tree that is published under
theurl_prefixparameter described earlier:filepath = views/staticIf the path is a relative path, (does not starts with a/), then the path
will be interpreted relatively from the ini file that holds the declaration.It is also possible to use an absolute file path:filepath = /Path/To/my/static/treeIt is also possible to specify a file path relative from a python egg:filepath = egg:my.package/views/staticThe above declaration will locate the import location of themy.packageegg,
and locate the tree from there, using the path section that follows the egg’s name.The parametersurl_prefixandfilepathneed to be defined together.Specifying and extending the composition of resourcesgf.rejuiceprovides a simple and flexible way to define how the production (compressed)
resources are produced from the development resources (the original sources).Specifying the composition of resourcesA list of javascript or css resources can be specified from the configuration file.
Anything ending with.jsor.csswill be considered to be a resource:default.min.js       = default.js
min/default.min.css  = custom/default.cssEach line contains two values here. The value on the left of the equal sign specifies the
path of the compressed, minified resource (the result of compression). The right value specifies the
path of original, uncompressed source (the origin of compression).All file paths are applied relatively from the root of the static
tree, specified byurl_prefixandfilepath.Derive a resource from a list of resourcesIt is also possible to produce one resource from a list of resources,
which, in that case will be compressed and concatenated to a single resource. The configuration
allows a list of resources in multiple lines:default.min.js =
    dependency.js
    dependency2.js
    default.js
min/default.min.css =
    a/css1.css
    b/css2.css
    custom/default.cssJuicer’s scheme for specifying resource dependencies from inside the resources (by using@importfrom css files and@dependsfrom commented parts of javascript files)
is also taken into consideration. But if such dependencies are specified from the
resources themselves, then they do not need to be (should not be) listed from the
configuration section as well.extend_resources: adding more static trees to the configurationFor more flexibility, it is possible to define the resource composition from a separate
configuration file section. In this case the filter section will refer to this section
with theextend_resourcesparameter:[filter:rejuice]
use = egg:gf.rejuice#develjuice
base_urls = http://127.0.0.1:6543/static
extend_resources = juice_resources

[juice_resources]
url_prefix = /static
filepath  = views/static

# Resource composition:
default.min.js       = default.js
min/default.min.css  = custom/default.cssTheextend_resourcesoption selects a section in the ini file,
that configures the actual resources to be considered for the transformation.Instead of a single section, a list of sections can also be specified:extend_resources =
    juice_resources_1
    juice_resources_2Extending resources from another configuration fileFollowing the same syntax thatPasteDeployuses for extending its
configuration, it is also possible to extend the resources from
a section residing in an arbitrary .ini file:extend_resources = config:another_config_file.ini#juice_resourcesThis makes it possible to separate the resource configuration forgf.rejuiceinto a separate file.In the filemy-devel.inithe following could be entered:[filter:rejuice]
use = egg:gf.rejuice#develjuice
base_urls = http://127.0.0.1:6543/static
extend_resources = config:another_config_file.ini#juice_resourcesWhile the referred fileanother_config_file.inicould look like this:[juice_resources]
url_prefix = /static
filepath  = egg:my.package/views/static

# Resource composition:
default.min.js       = default.js
min/default.min.css  = custom/default.cssThe config: prefix makes it possible to refer a configuration file
relatively from the location of the current configuration file:extend_resources = config:another_config_file.ini#juice_resourcesAlternately, an absolute file path can be used as well:extend_resources = config:/Path/To/my/place/another_config_file.ini#juice_resourcesFinally, the configuration file can be selected relative from a python egg import:extend_resources = egg:my.package/path/to/another_config_file.ini#juice_resourcesInstead of a single extension section, a list of extension sections can also
be specified:extend_resources =
    base_resources
    more_resources
    egg:my.package/path/to/another_config_file.ini#juice_resources_1
    egg:my.package/path/to/another_config_file.ini#juice_resources_2Putting the resource declarations to a separate file makes it easier to manage them. The
configuration file behaves just as expected from python configfiles or Paster configuration
files. The[DEFAULT]section can specify a default value for theurl_prefixand thefilepathparameters, which then each section in the same file inherits, or can overwrite:[DEFAULT]
url_prefix = /static
filepath  = egg:my.package/views/static

[juice_resources_1]
default.min.js       = default.js
min/default.min.css  = custom/default.css

[juice_resources_2]
some_other.min.js    = some_other.js
...

[juice_resources_3]
url_prefix = /static2
filepath  = egg:another.package/views/static

different.min.js     = different.js
...It is also possible, just as in every python config file, to use%(varname)style interpolation
anywhere inside the declarations.%(here)containing the full path of the current ini file, as
well as%(__name__)containing the name of the current section, are also available, just
as usual in the Paster configuration files.Importing resources from another static treeIt is an important need to construct resources from another static tree than the one the final
resource will be located in. Important use cases are: reusing the concatenation scheme of a
resource defined elsewhere, or, making a site resource out of resources produced by separate
javascript checkouts, or python eggs.A resource originating from another section can be specified by providing the section name,
following the resource path by one or more spaces:default.min.js = default.js othersectionIn a more realistic example, the resource would typically be produced from a list
of resources from separate sections:[main]
url_prefix = /static
filepath = egg:my.site/views/static

default.min.js =
    jquery-1.4.4.min.js       jquery_section
    jquery-ui-1.8.min.js      jquery-ui_section
    default.js
extend_resources =
    jquery_section
    jquery-ui_section

[jquery-section]
url_prefix = /static-jquery
filepath = egg:my.jquery/static

[jquery-ui_section]
url_prefix = /static-jquery-ui
filepath = egg:my.jqueryui/staticNote that all the sections used as an import source,mustalso be listed in theextend_resourceparameter. Failing to do so, will result in an error message.As seen from the above example, the resources imported from another section (jquery-1.4.4.min.js,
andjquery-ui-1.8.min.jsin this case) need not be specified as resources in that
section. They just need to be present in the source tree.If however the imported resources are not themselves compressed, they will be. Also, if they
contain further dependencies specified by@importor@depends, these dependencies will
properly be included in the resulting resource.If the referenced sections contain further resource concatenation rules, they will
be processed as well:[main]
url_prefix = /static
filepath = egg:my.site/views/static

default.min.js =
    jquery-1.4.4.min.js       jquery_section
    jquery-ui-1.8.min.js      jquery-ui_section
    default.js
extend_resources =
    jquery_section
    jquery-ui_section

[jquery-section]
url_prefix = /static-jquery
filepath = egg:my.jquery/static

jquery-1.4.4.min.js = jquery.1.4.4.js

[jquery-ui_section]
url_prefix = /static-jquery-ui
filepath = egg:my.jqueryui/static

jquery-ui-1.8.min.js =
    ui/jquery.ui.core.js
    ui/jquery.ui.widget.js
    ui/jquery.ui.mouse.js
    ...
    ui/jquery.effects.transfer.jsWhat will result from this configuration, that the middleware will explode every single
resource to its development resources, recursively. Also, runningbin/rejuice myconfig.ini mainwill produce all resources specified in all sections, providing a complete update
based on the original sources.To make the example even more realistic, let us suppose that each static tree
has a standalone configuration as well, which are referred from the configuration of the site.site.ini:[main]
url_prefix = /static
filepath = views/static

default.min.js =
    jquery-1.4.4.min.js     egg:my.jquery/juicer.ini#jquery_section
    jquery-ui-1.8.min.js    egg:my.jqueryui/juicer.ini#jquery-ui_section
    default.js
extend_resources =
    egg:my.jquery/juicer.ini#jquery_section
    egg:my.jqueryui/juicer.ini#jquery-ui_sectionjuicer.iniinmy.jqueryegg:[jquery-section]
url_prefix = /static-jquery
filepath = views/static

jquery-1.4.4.min.js = jquery.1.4.4.jsjuicer.iniinmy.jqueryuiegg:[jquery-ui_section]
url_prefix = /static-jquery-ui
filepath = views/static

jquery-ui-1.8.min.js =
    ui/jquery.ui.core.js
    ui/jquery.ui.widget.js
    ui/jquery.ui.mouse.js
    ...
    ui/jquery.effects.transfer.jsLocalizing images for imported css resourcesA valuable feature ofgf.rejuiceis the support of concatenation a single css resource
from foreign css resources, that is normally
not supported by Juicer and hard to be achieved by Juicer alone.Css resources containurl(…)directives to refer to other resources, typically images.
While Juicer takes care of rewriting the urls in these directives, so that the original
resources can be located from the compressed css, the scheme will
stop working as soon as a css file is composed from many different source trees or eggs.To support this use case,gf.rejuicecopies all images referenced in the compressed
css file, that reside in different source trees. This only happens if a css image
compresses from any resource from a section which has a differentfilepaththan
the target section.For example, in case of a following configuration:[main]
url_prefix = /static
filepath = egg:my.package/views/static
default.min.css =
    other_package.css        other_section
    default.css
extend_resources = other_section

[other_section]
url_prefix = /other-static
filepath = egg:other.package/staticif we runbin/rejuice myconfig.ini main, the result will be
adefault.min.cssfile, and adefault.min.css.imagesdirectory,
created in the same directory next to each other. Thedefault.min.css.imagesdirectory will contain a copy of all the resources thatdefault.min.cssreferences.As a consequence of this, any other sections than the main section, are only needed
for development mode while the middleware resolves urls to the original sources,
and for the time when the compression is done by runningbin/rejuice. Following
that, the other trees can be brought to offline as they are not needed to
be present in the production website at all, that operates solely on the
compressed resources.This makes it possible to establish a development scheme, when the production
websites contains the compressed resources only, and the library dependencies,
present in checked-out static trees or development eggs, are only present
during the time of development.Example use casesXXXChangelog for gf.rejuice0.2 (2011-03-08)Fix a bug in the resolver lists that prevented to resolve from any other
resolver then the first one in the list
[ree]Small refactoring, fix docs
[ree]0.1 (2011-03-04)Initial release
[ree]