Sphinx+reStructured: 内容复用设计与实践

2023-07-12  本文已影响0人  莉莉邓

缘起:近期有看到提问 “doc-as-code 方案下如何做内容复用?”,想想使用 Sphinx + reStructuredtext 已经有段时间了,也有一些个人认为还是比较实用和提升效率的做法,故趁机重新梳理、复盘一下,希望对有同样需求和问题的小伙伴有些启发和帮助。
P.S.:其实在之前的两篇文档里,也已经分别介绍过了复用实现方式的变量和条件文本的使用,可分别查看:
Sphinx+reStructuredText:变量的使用 - 简书 (jianshu.com)
Sphinx+reStructuredText:条件文本的使用 - 简书 (jianshu.com)

什么是内容复用

DITA Best Practice 一书中有这么一句话讲到内容复用,即 In the world of DITA, reuse is about writing content once and reusing that content wherever it is needed. 内容复用,确实具有诸多好处,比如:

那么,是不是如果没用DITA,是不是就没法实现内容复用呢?doc-as-code 方案中是否内容复用呢?本文想重点讨论这个问题,并分享一些笔者在实际工作过程中的经验。

根据内容模块的颗粒度,内容复用设计可以包括以下的情况:

不同颗粒度内容模块的复用设计与方法

针对词汇和短语类的信息片段

对于词汇、短语类的信息片段,可以考虑使用变量的方式处理。例如:

步骤:

Step 1conf.py 文件中定义全局 substitution。

rst_epilog = """

.. |product name| replace:: Product A 

"""

Step 2.rst 文件中使用已定义的 substitution。

|product name| provides a solution to xxxxxxxxxxxxxxxxxxxxxxxxxx. 

如遇需更新的情况,直接调整 conf.py 的表述即可。当然,如只需达到比如某些 rst (reStructuredText)文件中的统一,可以在 rst 文件中使用局部 substitution 声明变量值,而不在 conf.py 定义(相当于全局变量)。

当然,substitution 不仅仅能用于文本类变量的应用,也能用于图片或者可执行功能块类的变量应用。以图片为例,reStructuredText Markup Specification (sourceforge.io) 官方提供的用法示例如下:

West led the |H| 3, covered by dummy's |H| Q, East's |H| K,
and trumped in hand with the |S| 2.

.. |H| image:: /images/heart.png
   :height: 11
   :width: 11
.. |S| image:: /images/spade.png
   :height: 11
   :width: 11

* |Red light| means stop.
* |Green light| means go.
* |Yellow light| means go really fast.

.. |Red light|    image:: red_light.png
.. |Green light|  image:: green_light.png
.. |Yellow light| image:: yellow_light.png

|-><-| is the official symbol of POEE_.

.. |-><-| image:: discord.png
.. _POEE: http://www.poee.org/

此种用法下,再提供一种升级的用法,即结合 only directive,可以进一步实现相同参数名在不同产品/方案上不同的设定值。具体做法示例如下:

.. only:: tag_A
   
   .. |parameter_value| replace:: Value_A
 

.. only:: tag_B
   
   .. |parameter_value| replace:: Value_B


The Parameter value is  |parameter| 

需要注意的一点是,如果使用 only directive,需保证引入的 tag 必须在 conf.py 文件中进行过声明。声明命令如下:

# In conf.py file

tags.has ('tag_a')

后续结合不同场景需求,可过滤得到已定义 tag 后的内容。

针对段落类的信息块

在词汇或短语的基础上,如待重用的是内容较多的内容块,如段落、注意信息等,可以将待重用的内容保存为单独的文件,并使用 include directive 在需要重用的地方直接插入。
具体操作及代码可参考如下:

Step 1: 如需重用如下所示的 note 信息,可以将该 note 信息单独存储成一个文件,例如 note_a.txt 文件。

.. note:: This is a piece of note information, which can be shared for different topics. 

Step 2: 在需要重用的位置,使用 include directive 插入。

Paragraph example xxxxxxxxxxx. 

.. include:: note_a.txt 

针对主题类的模块化文档

对于内容规模更大些的,即差异较大的内容模块,更建议提供单独的主题文件,及独立的 rst 文件存放。在不同需求场景下,使用 toctree directive 调用不同的主题即可。
例如假定有如下的文档目录:

trunk
|--  product_A/
|     |-- introduction_product_A.rst
|--  product_B/
|     |-- introduction_product_B.rst
|--  shared/
|     |-- topic_1.rst
|     |-- topic_2.rst

那么针对产品A、产品B,在文档输出时,可以提供不同主题文件的引用,如:

# In index_A.rst  file

.. toctree:: 

   Introduction <product_A/introduction_product_A.rst>

# In index_B.rst  file

.. toctree:: 

   Introduction <product_B/introduction_product_B.rst>

# OR:  In a same toc or subtoc file, use *only* directive to filter 

.. only:: tag_A 

   .. toctree:: 

      Introduction <product_A/introduction_product_A.rst>

.. only:: tag_B

   .. toctree:: 

      Introduction <product_B/introduction_product_B.rst>

针对目录/多个主题集合

进一步地,除了单个的 Topic,待复用的是多个主题文件的集合,方法其实与主题文档的复用手段一样,区别在于可以定义整个 Sphinx project 的根目录下的 index.rst 文件,也即 root_doc (version 4.0 以前也叫master_doc )文件。

此时,可以在项目根目录下准备多个 index.rst 文件,并在不同发布场景的配置文件 conf.py 文件中声明具体使用哪个目录结构文件。

# In conf.py file

root_doc = index_product_A.rst

条件发布

在以上的内容复用实现示例中,不难发现已经引入了 conf.py, reStructuredText directives (substitution, only 等),那么在最后文档发布过程如何进行 tag 以及 root_doc 的过滤呢?此时,我们可以考虑灵活应用和扩展下 Makefile 文件。

在改 Makefile 文件之前,我们先来回顾下 Sphinx build 文档的基本命令。以 HTML 的输出为例,基本的 sphinx-build 命令如下:

sphinx-build -b html  source  build   -t  tag_option   -c  conf_file_path/

在上述命令中:

一旦引入了不同的 tag_optionconf.py 的组合,以达到不同发布物不同模版配置及内容过滤的需求。进一步地,可在 Makefile 中区分不同的发布命令,以便于其他协作者直接使用简单的 make 命令即可获得相应的发布物。

In Makefile file

# Set up the target for the HTML output of product A 
PA-HMTL:
          @$(SPHINXBUILD) -b html   "$(SOURCEDIR)"   "(BUILDDIR)/product_a"  -t  tag_a  -c  ../conf_a/

如是,使用 make PA-HTML 命令即可得到产品 A 的 HMTL 文档。在输出文件中,对于打了 tag 的内容,仅抓取标记为 tag_a 的内容。同时,应用了指定的 conf_a/conf.py 模版文件。在模版文件中,就可以自定义不同的样式设计、全局变量定义等等。

与 DITA 重用方式的比较

从上述内容不难发现,可以针对不同的内容颗粒度,灵活使用和组合不同的方式,以达到复用的目的。结合以前使用 DITA 的经验,将不同颗粒度的内容模块复用实现方式比较如下:

待重用的内容模块 DITA Sphinx + reStructuredText
词汇或短语 conref / keyref / conkeyref substitution directive
段落 conref include directive + only directive
主题 topicref toctree directive + only directive
目录结构 mapref root_doc + conf.py

写在最后

实际工作中,可能也会有小伙伴疑问 “既然已经是 doc-as-code 了,为啥要这么大费周章地考虑复用,最简单的复用方式,不是拉取分支(branch)吗?” 拉取分支固然是简单、直接的方式,但一旦分支(branch)分出后,分支管理、与主 trunk 的关系管理、冲突解决其实会是个比较难处理的问题,特别是对于非技术背景的 TW,一看到稍复杂的冲突可能就得头皮发麻、两手无措了。

另外,并不是提起内容复用,就一定只能有 DITA 的解决方案,在轻量级的 doc-as-code 模式下,也有相应的复用实现方法。但是,不论何种解决方案下,就内容复用本身,其也不应该是想起来复用就复用的。在决定复用内容之前,要经过全局的内容分析与设计,与团队内约定什么内容应该有限复用,何种场景下选择何种恰当的方式进行复用,以最小化的内容管理代价实现最大化复用效率。无论什么内容都想复用解决、或者一味为了复用而复用,都是不太可取的。

上一篇 下一篇

猜你喜欢

热点阅读