跳转到主要内容

渲染后资源包含

项目描述

资源库是一个Zope 3扩展,旨在使JavaScript、CSS和其他资源的包含变得简单、缓存友好且组件友好。

资源库

资源库旨在使JavaScript、CSS和其他资源的包含变得简单、缓存友好且组件友好。例如,如果一个页面上的两个小部件需要相同的JavaScript库,则该库应该只加载一次,但小部件设计者不需要关心其他小部件的存在。

假设一个小部件有一个虚构的Javascript库的副本。要配置该库为可用,请使用ZCML如下所示

>>> zcml("""
... <configure
...     xmlns="http://namespaces.zope.org/zope"
...     package="zc.resourcelibrary">
...   <include package="." file="meta.zcml" />
...   <resourceLibrary name="some-library">
...     <directory source="tests/example"/>
...   </resourceLibrary>
...
... </configure>
... """)

这正好等同于一个resourceDirectory标签,没有其他附加效果。

加载文件

还可以指定一个或多个JavaScript或CSS文件应该(通过引用)包含到需要库的页面HTML中。这是resourceLibrary和resourceDirectory之间的当前区别。

>>> zcml("""
... <configure
...     xmlns="http://namespaces.zope.org/zope"
...     package="zc.resourcelibrary">
...   <include package="." file="meta.zcml" />
...   <resourceLibrary name="my-lib">
...     <directory
...         source="tests/example/my-lib"
...         include="included.js included.css included.kss"
...     />
...   </resourceLibrary>
...
... </configure>
... """)

如果包含了一个资源库不理解的文件(即它既不是JavaScript也不是CSS),将发生异常。

>>> zcml("""
... <configure
...     xmlns="http://namespaces.zope.org/zope"
...     package="zc.resourcelibrary">
...   <include package="." file="meta.zcml" />
...   <resourceLibrary name="bad-lib">
...     <directory
...         source="tests/example/my-lib"
...         include="included.bad"
...     />
...   </resourceLibrary>
...
... </configure>
... """)
Traceback (most recent call last):
...
ConfigurationError: Resource library doesn't know how to include this file: "included.bad".
    File...

使用方法

组件通过使用特殊的TAL表达式来表明它们需要特定的资源库(JavaScript或其他)。(使用replace不是强制的,结果可以分配给一个虚拟变量,或者忽略。)

>>> zpt('<tal:block replace="resource_library:my-lib"/>')

我们将使用testbrowser.Browser来模拟用户查看网页。

>>> from zope.testbrowser.wsgi import Browser
>>> browser = Browser()
>>> browser.addHeader('Authorization', 'Basic mgr:mgrpw')
>>> browser.handleErrors = False

当请求一个不需要任何资源库的页面时,HTML将保持不变。

>>> browser.open('https://127.0.0.1/zc.resourcelibrary.test_template_1')
>>> browser.contents
'...<head></head>...'

当请求使用需要资源库的组件的页面时,库将在渲染的页面中被引用。

>>> browser.open('https://127.0.0.1/zc.resourcelibrary.test_template_2')

在HTML中插入了对JavaScript的引用。

>>> '/@@/my-lib/included.js' in browser.contents
True

并且可以通过引用的URL获取JavaScript。

>>> browser.open('/@@/my-lib/included.js')
>>> browser.headers['Content-Type']
'application/javascript'
>>> print(browser.contents.decode('ascii'))
    function be_annoying() {
    alert('Hi there!');
}

包含资源时使用带有命名空间的完整基本URL。

>>> browser.open('https://127.0.0.1/++skin++Basic/zc.resourcelibrary.test_template_2')
>>> print(browser.contents)
<html...
src="https://127.0.0.1/++skin++Basic/@@/my-lib/included.js"...
</html>

在HTML中也插入了对CSS的引用。

>>> browser.open('https://127.0.0.1/zc.resourcelibrary.test_template_2')
>>> '/@@/my-lib/included.css' in browser.contents
True

并且可以通过引用的URL获取CSS。

>>> browser.open('/@@/my-lib/included.css')
>>> browser.headers['Content-Type']
'text/css'
>>> print(browser.contents.decode('ascii'))
div .border {
    border: 1px silid black;
}

对未知库的引用将导致异常。

>>> browser.open('https://127.0.0.1/zc.resourcelibrary.test_template_3')
Traceback (most recent call last):
...
RuntimeError: Unknown resource library: "does-not-exist"

库的使用也可以通过编程方式指示。例如,如果一个页面本来不包含资源库...

>>> page = ('<html><head></head>'
...         '<body tal:define="unused view/doSomething">'
...         'This is the body.</body>')
>>> class View(object):
...     context = getRootFolder()
...     def doSomething(self):
...         pass
>>> zpt(page, view=View())
'...<head></head>...'

如果我们随后通过编程方式指示需要资源库,它将被包含。

>>> import zc.resourcelibrary
>>> class View(object):
...     context = getRootFolder()
...     def doSomething(self):
...         zc.resourcelibrary.need('my-lib')
>>> '/@@/my-lib/included.js' in zpt(page, view=View())
True

内容类型检查

资源只应从HTML和XML内容中引用,其他内容类型不应被资源库触摸。

>>> page = ('<html><head>'
...         '<tal:block replace="resource_library:my-lib"/>'
...         '</head><body></body></html>')
>>> '/@@/my-lib/included.js' in zpt(page, content_type='text/html')
True
>>> '/@@/my-lib/included.js' in zpt(page, content_type='text/xml')
True
>>> '/@@/my-lib/included.js' in zpt(page, content_type='text/none')
False

如果内容类型包含大写字母,这也适用,如RfC 2045中关于MIME类型语法的说明(我们目前还不能测试主要类型的 大写字母,因为出版商还没有完全遵循该细节的RfC)

>>> '/@@/my-lib/included.js' in zpt(page, content_type='text/hTMl')
True
>>> '/@@/my-lib/included.js' in zpt(page, content_type='text/nOne')
False

内容类型的参数也不能愚弄检查。

>>> '/@@/my-lib/included.js' in zpt(
...     page, content_type='text/xml; charset=utf-8')
True
>>> '/@@/my-lib/included.js' in zpt(
...     page, content_type='text/none; charset=utf-8')
False

然而,假设内容类型是一个严格有效的MIME类型规范,这意味着它不能包含任何空白字符,直到分号表示参数开始(我们无法测试主要类型周围的空白字符,因为这已经会困扰到出版商)

>>> '/@@/my-lib/included.js' in zpt(
...     page, content_type='text/ xml')
False
>>> '/@@/my-lib/included.js' in zpt(
...     page, content_type='text/xml ; charset=utf-8')
False

如果内容类型从未设置,它也可能是None,这当然也不算HTML或XML。

>>> from zc.resourcelibrary import publication
>>> from io import BytesIO
>>> request = publication.Request(body_instream=BytesIO(), environ={})
>>> request.response.setResult("This is not HTML text.")
>>> b'/@@/my-lib/included.js' in request.response.consumeBody()
False

依赖关系

如果资源库注册了对另一个库的依赖,则必须满足该依赖,否则将生成错误。

>>> zcml("""
... <configure
...     xmlns="http://namespaces.zope.org/zope"
...     package="zc.resourcelibrary">
...   <include package="." file="meta.zcml" />
...
...   <resourceLibrary name="dependent-but-unsatisfied" require="not-here">
...     <directory source="tests/example"/>
...   </resourceLibrary>
...
... </configure>
... """)
Traceback (most recent call last):
...
ConfigurationError:...Resource library "dependent-but-unsatisfied" has unsatisfied dependency on "not-here"...
...

当依赖项得到满足时,注册将成功。

>>> zcml("""
... <configure
...     xmlns="http://namespaces.zope.org/zope"
...     package="zc.resourcelibrary">
...   <include package="." file="meta.zcml" />
...
...   <resourceLibrary name="dependent" require="dependency">
...     <directory source="tests/example" include="1.js"/>
...   </resourceLibrary>
...
...   <resourceLibrary name="dependency">
...     <directory source="tests/example" include="2.css"/>
...   </resourceLibrary>
...
... </configure>
... """)

如果一个库依赖于另一个库,并且第一个库在页面上被引用,第二个库也将包含在渲染的HTML中。

>>> zpt('<tal:block replace="resource_library:dependent"/>')
>>> browser.open('https://127.0.0.1/zc.resourcelibrary.test_template_4')
>>> '/@@/dependent/1.js' in browser.contents
True
>>> '/@@/dependency/2.css' in browser.contents
True

顺序很重要,特别是对于js文件,因此依赖项应该出现在页面中依赖库之前。

>>> print(browser.contents.strip())
<html>...dependency/2.css...dependent/1.js...</html>

资源库可以只注册依赖项列表,而不指定任何资源。

当在模板的资源库语句中使用此类库时,只有其依赖项在最终的渲染页面中被引用。

>>> zcml("""
... <configure
...     xmlns="http://namespaces.zope.org/zope"
...     package="zc.resourcelibrary">
...   <include package="." file="meta.zcml" />
...
...   <resourceLibrary name="only_require" require="my-lib dependent"/>
...
... </configure>
... """)
>>> zpt('<tal:block replace="resource_library:only_require"/>')
>>> browser.open('https://127.0.0.1/zc.resourcelibrary.test_template_7')
>>> '/@@/my-lib/included.js' in browser.contents
True
>>> '/@@/my-lib/included.css' in browser.contents
True
>>> '/@@/dependent/1.js' in browser.contents
True
>>> '/@@/dependency/2.css' in browser.contents
True
>>> '/@@/only_require' in browser.contents
False

错误条件

如果你做错了事情,将会报告错误。

>>> zcml("""
... <configure
...     xmlns="http://namespaces.zope.org/zope"
...     package="zc.resourcelibrary">
...   <include package="." file="meta.zcml" />
...
...   <resourceLibrary name="some-library">
...     <directory source="does-not-exist"/>
...   </resourceLibrary>
...
... </configure>
... """)
Traceback (most recent call last):
...
ConfigurationError: Directory u'...does-not-exist' does not exist
    File...

多头问题

偶尔,HTML文档的主体可能包含文本“<head>”。在这些情况下,只应操作实际的head标签。第一次出现的“<head>”将插入脚本标签...

>>> browser.open('https://127.0.0.1/zc.resourcelibrary.test_template_5')
>>> print(browser.contents)
<html>...<head> <script src="https://127.0.0.1/@@/my-lib/included.js"...

…但是这是唯一插入的时候。

>>> browser.contents.count('src="https://127.0.0.1/@@/my-lib/included.js"')
1

发布过程中的错误

请注意,如果在发布过程中抛出异常,资源库将被禁用。

>>> browser.handleErrors = True
>>> browser.post(
...    'https://127.0.0.1/zc.resourcelibrary.test_template_5',
...    'value:int=dummy', 'multipart/form-data')
Traceback (most recent call last):
 ...
urllib.error.HTTPError: ...
>>> '/@@/my-lib/included.js' in browser.contents
False

自定义“目录”工厂

默认情况下,当使用目录指令时,将创建一个资源目录。你可以添加一个工厂选项来指定不同的资源目录工厂。这可以用于,例如,提供动态资源。

>>> zcml("""
... <configure
...     xmlns="http://namespaces.zope.org/zope"
...     package="zc.resourcelibrary">
...   <include package="." file="meta.zcml" />
...
...   <resourceLibrary name="my-lib">
...     <directory
...         source="tests/example/my-lib"
...         include="foo.js"
...         factory="zc.resourcelibrary.tests.tests.TestFactory"
...     />
...   </resourceLibrary>
...
... </configure>
... """, clear=['my-lib'])

工厂将使用源目录、安全检查器和名称被调用。我们已经创建了一个实现动态资源目录的类。

>>> browser.open('https://127.0.0.1/zc.resourcelibrary.test_template_2')
>>> '/@@/my-lib/foo.js' in browser.contents
True
>>> browser.open('https://127.0.0.1/@@/my-lib/foo.js')
>>> print(browser.contents)
foo = 1;

库插入位置标记

您可以显式标记插入HTML的位置。要做到这一点,请在模板中添加特殊注释“<!– zc.resourcelibrary –>”(精确字符串,不带引号)。在处理过程中,它将被资源库HTML替换。

>>> browser.open('https://127.0.0.1/zc.resourcelibrary.test_template_6')

在HTML中插入了对JavaScript的引用。

>>> print(browser.contents)
<html>
  <head>
    <title>Marker test</title>
<BLANKLINE>
    <!-- Libraries will be included below -->
    <script src="https://127.0.0.1/@@/my-lib/foo.js"
        type="text/javascript">
    </script>
  </head>
...
</html>

未来工作

  • 我们希望能够指定添加到资源中的单个文件。

  • 我们可能需要能够用不同的文件覆盖资源中的文件。

  • 当前每个库只允许一个<directory>标签。如果允许多个标签,它们应该合并还是有不同的前缀?

  • 添加一个测试以确保文件只包含一次,并且顺序正确。

变更记录

2.1.0 (2018-10-19)

  • 添加对Python 3.7的支持。

2.0.0 (2017-05-23)

  • 添加对Python 3.4、3.5、3.6和PyPy的支持。

  • 删除对zope.app.testingzope.app.zcmlfiles等测试依赖项的依赖。

  • 将zope.app.publication依赖项设置为可选。

1.3.4 (2012-01-20)

1.3.2 (2010-08-16)

  • Response._addDependencies将仅在ResourceLibrary实际包含资源时将其包含在依赖项列表中。

    这使得只声明对其他库的依赖项的指令再次工作。

  • 添加缺少的依赖项zope.app.pagetemplate,清理未使用的导入和空白。

1.3.1 (2010-03-24)

  • 在重试请求期间所需的资源库现在已正确注册并注入到HTML中。

  • 从zope.site移动到zope.component后,导入钩子功能。这消除了对zope.site的依赖。

  • 删除了一个未使用的ISite导入,从而消除了对zope.location的未声明依赖。

1.3.0 (2009-10-08)

  • 使用zope.browserresource而不是zope.app.publisher,删除了对后者的依赖。

  • 通过查询MultiAdapter查找“资源视图”而不是查找适配器注册表。

  • 将zope.site的依赖项移动到测试依赖项中。

1.2.0 (2009-06-04)

  • 使用zope.site而不是zope.app.component。这消除了对zope.app.component的直接依赖。

1.1.0 (2009-05-05)

新功能

  • 现在尝试使用“资源视图”生成资源URL;如果失败,我们将回退到以前的手动从站点URL构建URL的方法。这确保了资源库遵守现有的资源发布插件点(见zope.app.publisher.browser.resources)。

  • 现在可以使用特殊标记注释‘<!– zc.resourcelibrary –>’显式指定资源链接的插入位置。

1.0.2 (2009-01-27)

  • 从依赖项中删除zope.app.zapi,并用直接导入替换其使用。

  • 使用zope.org上的zope-dev邮件列表地址而不是zope3-dev,因为后者已停用。

  • 将包主页上的“cheeseshop”更改为“pypi”。

1.0.1 (2008-03-07)

已修复的错误

  • 添加了来自标准Zope 3响应的行为,以猜测一个没有显式MIME类型的非HTML主体应具有‘text/plain’ MIME类型。这意味着,例如,没有显式内容类型且主体为空的重定向将不再在资源库响应代码中引发异常。

1.0.0 (2008-02-17)

新功能

  • 现在可以提供替代的“目录-资源”工厂。这有助于实现动态资源。

已修复的错误

  • 更新了功能测试zcml文件,以消除弃用警告。

0.8.2 (2007-12-07)

  • bug修复:在检查内容类型时,考虑它可能为None

0.8.1 (2007-12-05)

  • 更改了MIME类型处理,使其对空白更加严格,以符合RFC 2045

0.8 (2007-12-04)

  • 修复了检查HTML和XML内容,以允许内容类型参数

0.6.1 (2007-11-03)

  • 更新包元数据。

  • 修复了包依赖关系。

  • 合并了功能测试和单元测试。

0.6.0 (2006-09-22)

???

0.5.2 (2006-06-15)

  • 添加了更多包元数据。

0.5.1 (2006-06-06)

  • 更新包代码以与其他包的新版本兼容。

0.5.0 (2006-04-24)

  • 初始发布。

下载文件

下载适用于您平台的文件。如果您不确定选择哪一个,请了解更多关于安装包的信息。

源分布

zc.resourcelibrary-2.1.0.tar.gz (29.8 kB 查看散列值)

上传时间

构建分布

zc.resourcelibrary-2.1.0-py2.py3-none-any.whl (30.9 kB 查看散列值)

上传时间 Python 2 Python 3

由以下机构支持