summaryrefslogtreecommitdiff
path: root/docs
diff options
context:
space:
mode:
authorAndrew Shadura <andrew@shadura.me>2015-08-20 15:58:26 +0200
committerAndrew Shadura <andrew@shadura.me>2015-08-20 15:58:26 +0200
commitff1408420159488a106492ccd11dd234967029b6 (patch)
tree473420cee1c5229a427ec4cafead1aa6c0a26800 /docs
Imported Upstream version 0.1.29
Diffstat (limited to 'docs')
-rw-r--r--docs/CHANGELOG5
-rw-r--r--docs/COPYRIGHT5
-rw-r--r--docs/LICENSE165
-rw-r--r--docs/Makefile130
-rw-r--r--docs/source/conf.py51
-rw-r--r--docs/source/docs/architecture.rst10
-rw-r--r--docs/source/docs/architecture/bound.rst131
-rw-r--r--docs/source/docs/architecture/components.rst90
-rw-r--r--docs/source/docs/architecture/config.rst43
-rw-r--r--docs/source/docs/architecture/trees.rst197
-rw-r--r--docs/source/docs/quickstart.rst96
-rw-r--r--docs/source/index.rst46
-rw-r--r--docs/source/ref/reconfigure.builders.rst6
-rw-r--r--docs/source/ref/reconfigure.configs.rst8
-rw-r--r--docs/source/ref/reconfigure.includers.rst6
-rw-r--r--docs/source/ref/reconfigure.items.bound.rst6
-rw-r--r--docs/source/ref/reconfigure.nodes.rst6
-rw-r--r--docs/source/ref/reconfigure.parsers.rst6
18 files changed, 1007 insertions, 0 deletions
diff --git a/docs/CHANGELOG b/docs/CHANGELOG
new file mode 100644
index 0000000..394d5e2
--- /dev/null
+++ b/docs/CHANGELOG
@@ -0,0 +1,5 @@
+python-reconfigure (__VERSION__) UNRELEASED; urgency=low
+
+ * Initial release.
+
+ -- Eugeny Pankov <e@ajenti.org> Thu, 7 Feb 2013 00:12:00 +0300
diff --git a/docs/COPYRIGHT b/docs/COPYRIGHT
new file mode 100644
index 0000000..a3ccff0
--- /dev/null
+++ b/docs/COPYRIGHT
@@ -0,0 +1,5 @@
+Copyright (c) 2012-2013 The Ajenti Team
+* Eugeny Pankov <john.pankov@gmail.com> & contributors
+
+LICENSED UNDER LGPLv3
+See LICENSE.
diff --git a/docs/LICENSE b/docs/LICENSE
new file mode 100644
index 0000000..65c5ca8
--- /dev/null
+++ b/docs/LICENSE
@@ -0,0 +1,165 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+ This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+ 0. Additional Definitions.
+
+ As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
+
+ "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+ An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+ A "Combined Work" is a work produced by combining or linking an
+Application with the Library. The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+ The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+ The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+ 1. Exception to Section 3 of the GNU GPL.
+
+ You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+ 2. Conveying Modified Versions.
+
+ If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+ a) under this License, provided that you make a good faith effort to
+ ensure that, in the event an Application does not supply the
+ function or data, the facility still operates, and performs
+ whatever part of its purpose remains meaningful, or
+
+ b) under the GNU GPL, with none of the additional permissions of
+ this License applicable to that copy.
+
+ 3. Object Code Incorporating Material from Library Header Files.
+
+ The object code form of an Application may incorporate material from
+a header file that is part of the Library. You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+ a) Give prominent notice with each copy of the object code that the
+ Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the object code with a copy of the GNU GPL and this license
+ document.
+
+ 4. Combined Works.
+
+ You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+ a) Give prominent notice with each copy of the Combined Work that
+ the Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the Combined Work with a copy of the GNU GPL and this license
+ document.
+
+ c) For a Combined Work that displays copyright notices during
+ execution, include the copyright notice for the Library among
+ these notices, as well as a reference directing the user to the
+ copies of the GNU GPL and this license document.
+
+ d) Do one of the following:
+
+ 0) Convey the Minimal Corresponding Source under the terms of this
+ License, and the Corresponding Application Code in a form
+ suitable for, and under terms that permit, the user to
+ recombine or relink the Application with a modified version of
+ the Linked Version to produce a modified Combined Work, in the
+ manner specified by section 6 of the GNU GPL for conveying
+ Corresponding Source.
+
+ 1) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (a) uses at run time
+ a copy of the Library already present on the user's computer
+ system, and (b) will operate properly with a modified version
+ of the Library that is interface-compatible with the Linked
+ Version.
+
+ e) Provide Installation Information, but only if you would otherwise
+ be required to provide such information under section 6 of the
+ GNU GPL, and only to the extent that such information is
+ necessary to install and execute a modified version of the
+ Combined Work produced by recombining or relinking the
+ Application with a modified version of the Linked Version. (If
+ you use option 4d0, the Installation Information must accompany
+ the Minimal Corresponding Source and Corresponding Application
+ Code. If you use option 4d1, you must provide the Installation
+ Information in the manner specified by section 6 of the GNU GPL
+ for conveying Corresponding Source.)
+
+ 5. Combined Libraries.
+
+ You may place library facilities that are a work based on the
+Library side by side in a single library together with other library
+facilities that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+ a) Accompany the combined library with a copy of the same work based
+ on the Library, uncombined with any other library facilities,
+ conveyed under the terms of this License.
+
+ b) Give prominent notice with the combined library that part of it
+ is a work based on the Library, and explaining where to find the
+ accompanying uncombined form of the same work.
+
+ 6. Revised Versions of the GNU Lesser General Public License.
+
+ The Free Software Foundation may publish revised and/or new versions
+of the GNU Lesser General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Library as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+ If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.
diff --git a/docs/Makefile b/docs/Makefile
new file mode 100644
index 0000000..4b9db37
--- /dev/null
+++ b/docs/Makefile
@@ -0,0 +1,130 @@
+# Makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line.
+SPHINXOPTS =
+SPHINXBUILD = sphinx-build
+PAPER =
+BUILDDIR = build
+
+# Internal variables.
+PAPEROPT_a4 = -D latex_paper_size=a4
+PAPEROPT_letter = -D latex_paper_size=letter
+ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source
+
+.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest
+
+help:
+ @echo "Please use \`make <target>' where <target> is one of"
+ @echo " html to make standalone HTML files"
+ @echo " dirhtml to make HTML files named index.html in directories"
+ @echo " singlehtml to make a single large HTML file"
+ @echo " pickle to make pickle files"
+ @echo " json to make JSON files"
+ @echo " htmlhelp to make HTML files and a HTML help project"
+ @echo " qthelp to make HTML files and a qthelp project"
+ @echo " devhelp to make HTML files and a Devhelp project"
+ @echo " epub to make an epub"
+ @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
+ @echo " latexpdf to make LaTeX files and run them through pdflatex"
+ @echo " text to make text files"
+ @echo " man to make manual pages"
+ @echo " changes to make an overview of all changed/added/deprecated items"
+ @echo " linkcheck to check all external links for integrity"
+ @echo " doctest to run all doctests embedded in the documentation (if enabled)"
+
+clean:
+ -rm -rf $(BUILDDIR)/*
+
+html:
+ $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
+ @echo
+ @echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
+
+dirhtml:
+ $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
+ @echo
+ @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
+
+singlehtml:
+ $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
+ @echo
+ @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
+
+pickle:
+ $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
+ @echo
+ @echo "Build finished; now you can process the pickle files."
+
+json:
+ $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
+ @echo
+ @echo "Build finished; now you can process the JSON files."
+
+htmlhelp:
+ $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
+ @echo
+ @echo "Build finished; now you can run HTML Help Workshop with the" \
+ ".hhp project file in $(BUILDDIR)/htmlhelp."
+
+qthelp:
+ $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
+ @echo
+ @echo "Build finished; now you can run "qcollectiongenerator" with the" \
+ ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
+ @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Ajenti.qhcp"
+ @echo "To view the help file:"
+ @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Ajenti.qhc"
+
+devhelp:
+ $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
+ @echo
+ @echo "Build finished."
+ @echo "To view the help file:"
+ @echo "# mkdir -p $$HOME/.local/share/devhelp/Ajenti"
+ @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Ajenti"
+ @echo "# devhelp"
+
+epub:
+ $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
+ @echo
+ @echo "Build finished. The epub file is in $(BUILDDIR)/epub."
+
+latex:
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+ @echo
+ @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
+ @echo "Run \`make' in that directory to run these through (pdf)latex" \
+ "(use \`make latexpdf' here to do that automatically)."
+
+latexpdf:
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+ @echo "Running LaTeX files through pdflatex..."
+ make -C $(BUILDDIR)/latex all-pdf
+ @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
+
+text:
+ $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
+ @echo
+ @echo "Build finished. The text files are in $(BUILDDIR)/text."
+
+man:
+ $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
+ @echo
+ @echo "Build finished. The manual pages are in $(BUILDDIR)/man."
+
+changes:
+ $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
+ @echo
+ @echo "The overview file is in $(BUILDDIR)/changes."
+
+linkcheck:
+ $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
+ @echo
+ @echo "Link check complete; look for any errors in the above output " \
+ "or in $(BUILDDIR)/linkcheck/output.txt."
+
+doctest:
+ $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
+ @echo "Testing of doctests in the sources finished, look at the " \
+ "results in $(BUILDDIR)/doctest/output.txt."
diff --git a/docs/source/conf.py b/docs/source/conf.py
new file mode 100644
index 0000000..1cbb69c
--- /dev/null
+++ b/docs/source/conf.py
@@ -0,0 +1,51 @@
+# -*- coding: utf-8 -*-
+import sys
+import os
+
+sys.path.insert(0, os.path.abspath('../..'))
+
+extensions = ['sphinx.ext.autodoc', 'sphinx.ext.ifconfig', 'sphinx.ext.viewcode'] # 'sphinx.ext.intersphinx']
+
+templates_path = ['_templates']
+
+source_suffix = '.rst'
+
+#source_encoding = 'utf-8-sig'
+
+master_doc = 'index'
+
+project = u'Reconfigure'
+copyright = u'2013, Eugeny Pankov'
+
+version = '1.0'
+release = '1.0a1'
+
+exclude_patterns = []
+add_function_parentheses = True
+
+pygments_style = 'sphinx'
+
+# A list of ignored prefixes for module index sorting.
+#modindex_common_prefix = []
+
+
+# -- Options for HTML output ---------------------------------------------------
+html_theme = 'air'
+#html_theme_options = {}
+html_theme_path = ['../../../sphinx-themes']
+
+html_title = 'Reconfigure documentation'
+html_short_title = 'Reconfigure docs'
+
+#html_logo = None
+
+#html_favicon = None
+
+html_static_path = ['_static']
+
+htmlhelp_basename = 'Reconfiguredoc'
+
+
+# Example configuration for intersphinx: refer to the Python standard library.
+intersphinx_mapping = {'http://docs.python.org/': None}
+
diff --git a/docs/source/docs/architecture.rst b/docs/source/docs/architecture.rst
new file mode 100644
index 0000000..34247ab
--- /dev/null
+++ b/docs/source/docs/architecture.rst
@@ -0,0 +1,10 @@
+Architecture
+************
+
+.. toctree::
+ :maxdepth: 2
+
+ architecture/trees
+ architecture/bound
+ architecture/components
+ architecture/config
diff --git a/docs/source/docs/architecture/bound.rst b/docs/source/docs/architecture/bound.rst
new file mode 100644
index 0000000..e59b951
--- /dev/null
+++ b/docs/source/docs/architecture/bound.rst
@@ -0,0 +1,131 @@
+.. _Bound data:
+
+Bound Data
+**********
+
+Bound data (:class:`reconfigure.items.bound.BoundData`) is a special class that can be subclassed and stuffed with properties, which will act as proxies to an underlying :ref:`Node tree<node-tree>`. This can be confusing, so let's go with an example::
+
+
+ >>> from reconfigure.nodes import Node, PropertyNode
+ >>> from reconfigure.items.bound import BoundData
+ >>>
+ >>> node = Node('test')
+ >>> node.append(PropertyNode('name', 'Alice'))
+ >>> node.append(PropertyNode('age', '25'))
+ >>> node.append(PropertyNode('gender', 'f'))
+ >>> print node
+ (test)
+ name = Alice
+ age = 25
+ gender = f
+
+Here we have a very simple :ref:`Node tree <node-tree>`.
+Note that all values are ``str`` and the ``gender`` is coded in a single character (we have probably parsed this tree from some .ini file).
+Now let's define a BoundData class::
+
+ >>> class HumanData (BoundData):
+ ... pass
+ ...
+ >>> HumanData.bind_property('name', 'name')
+ >>> HumanData.bind_property('age', 'age', getter=int, setter=str)
+ >>> HumanData.bind_property('gender', 'gender',
+ ... getter=lambda x: 'Male' if x == 'm' else 'Female',
+ ... setter=lambda x: 'm' if x == 'Male' else 'f')
+
+ >>> human = HumanData(node)
+ >>> human
+ <__main__.MyData object at 0x114ddd0>
+ >>> print human
+ {
+ "gender": "Female",
+ "age": 25,
+ "name": "Alice"
+ }
+
+First, we've defined our ``BoundData`` subclass. Then, we have defined three properties in it:
+
+ * ``name`` is the simplest property, it's directly bound to "name" child ``PropertyNode``
+ * ``age`` also has a getter and setter. These are invoked when the property is read or written. In this case, we use ``int()`` to parse a number from the node tree and ``str()`` to stringify it when writing back.
+ * ``gender`` is similar to ``age`` but has more complex getter and setter that transform "m" and "f" to a human-readable description.
+
+When the properties are mutated, the modifications are applied to Node tree immediately and vice versa::
+
+ >>> human.age
+ 25
+ >>> human.age = 30
+ >>> node.get('age').value
+ '30'
+ >>> node.get('age').value = 27
+ >>> human.age
+ 27
+
+
+Using collections
+=================
+
+Let's try a more complex node tree::
+
+ >>> nodes = Node('',
+ ... Node('Alice',
+ ... PropertyNode('Phone', '1234-56-78')
+ ... ),
+ ... Node('Bob',
+ ... PropertyNode('Phone', '8765-43-21')
+ ... )
+ ... )
+ >>> print nodes
+ ()
+ (Alice)
+ Phone = 1234-56-78
+ (Bob)
+ Phone = 8765-43-21
+
+Bound data classes::
+
+ >>> class PersonData (BoundData):
+ ... def template(self, name, phone):
+ ... return Node(name,
+ ... PropertyNode('Phone', phone)
+ ... )
+ ...
+ >>> class PhonebookData (BoundData):
+ ... pass
+ ...
+ >>> PersonData.bind_property('Phone', 'phone')
+ >>> PersonData.bind_name('name')
+ >>>
+ >>> PhonebookData.bind_collection('entries', item_class=PersonData)
+ >>>
+ >>> phonebook = PhonebookData(nodes)
+ >>> print phonebook
+ {
+ "entries": [
+ {
+ "phone": "1234-56-78",
+ "name": "Alice"
+ },
+ {
+ "phone": "8765-43-21",
+ "name": "Bob"
+ }
+ ]
+ }
+
+Here, ``bind_collection`` method is used to create a collection property from child nodes. ``item_class`` class will be used to wrap these nodes.
+
+Alternatively, you can employ :class:`reconfigure.items.bound.BoundDictionary` class to create a dict-like property::
+
+ >>> PhonebookData.bind_collection('entries', collection_class=BoundDictionary, item_class=PersonData, key=lambda x: x.name)
+ >>> print phonebook
+ {
+ "entries": {
+ "Bob": {
+ "phone": "8765-43-21",
+ "name": "Bob"
+ },
+ "Alice": {
+ "phone": "1234-56-78",
+ "name": "Alice"
+ }
+ }
+ } \ No newline at end of file
diff --git a/docs/source/docs/architecture/components.rst b/docs/source/docs/architecture/components.rst
new file mode 100644
index 0000000..072a6d0
--- /dev/null
+++ b/docs/source/docs/architecture/components.rst
@@ -0,0 +1,90 @@
+Components
+**********
+
+.. _parser:
+
+Parsers
+=======
+
+Parsers are :class:`reconfigure.parsers.BaseParser` subclasses which transform :ref:`raw config content <raw-config>` into :ref:`node trees <node-tree>` and vice versa
+
+Making your own parser is as easy as subclassing :class:`reconfigure.parsers.BaseParser` and overriding ``parse`` and ``stringify`` methods.
+
+
+.. _includer:
+
+Includers
+=========
+
+Includers are used to handle the "include" directives in config files. Includers assemble the config file by finding the included files and parsing them and attaching them to the :ref:`node tree <node-tree>` of the main config. Reconfigure keeps track of which node belongs to which file by setting ``origin`` attribute on the included nodes
+
+Example of includer in action:
+
+ >>> from reconfigure.parsers import *
+ >>> from reconfigure.includers import *
+ >>> parser = IniFileParser()
+ >>> includer = SupervisorIncluder(parser)
+ >>> nodes = parser.parse(open('/etc/supervisor/supervisord.conf').read())
+ >>> print nodes
+ (None)
+ (unix_http_server)
+ file = /var/run//supervisor.sock ((the path to the socket file))
+ chmod = 0700 (sockef file mode (default 0700))
+ (supervisord)
+ logfile = /var/log/supervisor/supervisord.log ((main log file;default $CWD/supervisord.log))
+ pidfile = /var/run/supervisord.pid ((supervisord pidfile;default supervisord.pid))
+ childlogdir = /var/log/supervisor (('AUTO' child log dir, default $TEMP))
+ (rpcinterface:supervisor)
+ supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
+ (supervisorctl)
+ serverurl = unix:///var/run//supervisor.sock (use a unix:// URL for a unix socket)
+ (include)
+ files = /etc/supervisor/conf.d/*.conf
+
+Note the "include" node in the end. Now we'll run an includer over this tree::
+
+ >>> nodes = includer.compose('/etc/supervisor/supervisord.conf', nodes)
+ >>> print nodes
+ (None)
+ (unix_http_server)
+ file = /var/run//supervisor.sock ((the path to the socket file))
+ chmod = 0700 (sockef file mode (default 0700))
+ (supervisord)
+ logfile = /var/log/supervisor/supervisord.log ((main log file;default $CWD/supervisord.log))
+ pidfile = /var/run/supervisord.pid ((supervisord pidfile;default supervisord.pid))
+ childlogdir = /var/log/supervisor (('AUTO' child log dir, default $TEMP))
+ (rpcinterface:supervisor)
+ supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
+ (supervisorctl)
+ serverurl = unix:///var/run//supervisor.sock (use a unix:// URL for a unix socket)
+ <include> /etc/supervisor/conf.d/*.conf
+ (program:test)
+ command = cat
+
+Note how the include directive has turned into a junction point (:class:`reconfigure.nodes.IncludeNode`) and content of included files was parsed and attached.
+
+Calling ``decompose`` method will split the tree back into separate files:
+
+ >>> includer.decompose(nodes)
+ {
+ '/etc/supervisor/conf.d/1.conf': <reconfigure.nodes.RootNode object at 0x2c5cf10>,
+ '/etc/supervisor/supervisord.conf': <reconfigure.nodes.RootNode object at 0x2c5cb50>
+ }
+
+Writing your own includer
+-------------------------
+
+If you're up to writing a custom includer, take a look at :class:`reconfigure.includers.AutoIncluder`. It already implements the tree-walking and attachment logic, so you only need to implement two methods:
+
+ * ``is_include(node)``: should check if the ``node`` is an include directive for this file format, and if it is, return a glob (wildcard) or path to the included files
+ * ``remove_include(include_node)``: given an :class:`reconfigure.nodes.IncludeNode`, should transform it back into file-format-specific include directive and return it (as a :ref:`node tree <node-tree>` chunk)
+
+
+.. _builder:
+
+Builders
+========
+
+Builders transform :ref:`node trees <node-tree>` into :ref:`data trees <data-tree>`.
+
+To write your own builder, subclass :class:`reconfigure.builders.BaseBuilder` and override ``build`` and ``unbuild`` methods. \ No newline at end of file
diff --git a/docs/source/docs/architecture/config.rst b/docs/source/docs/architecture/config.rst
new file mode 100644
index 0000000..768a7ad
--- /dev/null
+++ b/docs/source/docs/architecture/config.rst
@@ -0,0 +1,43 @@
+.. _reconfig:
+
+Reconfig objects
+****************
+
+:class:`reconfigure.config.Reconfig` objects are pre-set pipelines connecting :ref:`Parsers <parser>`, :ref:`Includers <includer>` and :ref:`Builders <builder>`
+
+Reconfigure comes with many Reconfig objects out-of-the-box - see :ref:`reconfigure.configs`
+
+Writing your Reconfig subclass
+==============================
+
+Use the following pattern::
+
+ class <name>Config (Reconfig):
+ """
+ <description>
+ """
+
+ def __init__(self, **kwargs):
+ k = {
+ 'parser': <parser-class>(),
+ 'includer': <includer-class>(),
+ 'builder': BoundBuilder(<root-data-class>),
+ }
+ k.update(kwargs)
+ Reconfig.__init__(self, **k)
+
+Example::
+
+ class SupervisorConfig (Reconfig):
+ """
+ ``/etc/supervisor/supervisord.conf``
+ """
+
+ def __init__(self, **kwargs):
+ k = {
+ 'parser': IniFileParser(),
+ 'includer': SupervisorIncluder(),
+ 'builder': BoundBuilder(SupervisorData),
+ }
+ k.update(kwargs)
+ Reconfig.__init__(self, **k)
diff --git a/docs/source/docs/architecture/trees.rst b/docs/source/docs/architecture/trees.rst
new file mode 100644
index 0000000..9f3bd1f
--- /dev/null
+++ b/docs/source/docs/architecture/trees.rst
@@ -0,0 +1,197 @@
+Trees
+*****
+
+Reconfigure operates with three types of data:
+
+* Raw config text
+* Syntax tree
+* Data tree
+
+.. _raw-config:
+
+Config text
+===========
+
+This is a raw content, as read from the config file. It is fed to :ref:`Parsers <parser>` to produce the :ref:`Syntax trees<node-tree>`.
+
+.. _node-tree:
+
+Syntax trees
+==========
+
+Syntax tree is an object tree built from :class:`reconfigure.nodes.Node` objects, representing the syntax structure of the file. This is very similar to Abstract Syntax Trees.
+
+Syntax trees are produced by :ref:`Parser` classes.
+
+Example::
+
+ >>> text = open('/etc/samba/smb.conf').read()
+ >>> text
+ '#\n# Sample configuration file for the Samba suite for Debian GNU/Linux.\
+ ...
+ >>> from reconfigure.parsers import IniFileParser
+ >>> parser = IniFileParser()
+ >>> node_tree = parser.parse(text)
+ >>> print node_tree
+ (None)
+ (global)
+ workgroup = WORKGROUP
+ server string = %h server (Samba, Ubuntu)
+ dns proxy = no
+ log file = /var/log/samba/log.%m
+ max log size = 1000
+ syslog = 0
+ panic action = /usr/share/samba/panic-action %d
+ encrypt passwords = true
+ passdb backend = tdbsam
+ obey pam restrictions = yes
+ unix password sync = yes
+ passwd program = /usr/bin/passwd %u
+ passwd chat = *Enter\snew\s*\spassword:* %n\n *Retype\snew\s*\spassword:* %n\n *password\supdated\ssuccessfully* .
+ pam password change = yes
+ map to guest = bad user
+ usershare allow guests = yes
+ (printers)
+ comment = All Printers
+ browseable = no
+ path = /var/spool/samba
+ printable = yes
+ guest ok = no
+ read only = yes
+ create mask = 0700
+ >>> node_tree
+ <reconfigure.nodes.RootNode object at 0x219a150>
+ >>> node_tree.children[0]
+ <reconfigure.nodes.Node object at 0x219a950>
+ >>> node_tree.children[0].name
+ 'global'
+ >>> node_tree.children[0].children[0]
+ <reconfigure.nodes.PropertyNode object at 0x219aa10>
+ >>> node_tree.children[0].children[0].name
+ 'workgroup'
+ >>> node_tree.children[0].children[0].value
+ 'WORKGROUP'
+
+:class:`reconfigure.nodes.Node` reference page contains more information on how to manipulate node trees.
+
+Parsers work both ways - you can call ``stringify()`` and get the text representation back. Even more, you can feed the node tree to *another* parser and get the config in other format::
+
+ >>> from reconfigure.parsers import JsonParser
+ >>> json_parser = JsonParser()
+ >>> json_parser.stringify(node_tree)
+ >>> print json_parser.stringify(node_tree)
+ {
+ "global": {
+ "encrypt passwords": "true",
+ "pam password change": "yes",
+ "passdb backend": "tdbsam",
+ "passwd program": "/usr/bin/passwd %u",
+ ...
+ },
+ "print$": {
+ "comment": "Printer Drivers",
+ "path": "/var/lib/samba/printers",
+ "read only": "yes",
+ ...
+
+Syntax trees might look useful to you, but they are not nearly as cool as :ref:`Data trees <data-tree>`
+
+.. _data-tree:
+
+Data trees
+==========
+
+Data tree represents the actual, meaningful ideas stored in the config. Straight to example::
+
+ >>> from reconfigure.builders import BoundBuilder
+ >>> from reconfigure.items.samba import SambaData
+ >>> builder = BoundBuilder(SambaData)
+ >>> data_tree = builder.build(node_tree)
+ >>> data_tree
+ {
+ "global": {
+ "server_string": "%h server (Samba, Ubuntu)",
+ "workgroup": "WORKGROUP",
+ "interfaces": "",
+ "bind_interfaces_only": true,
+ "security": "user",
+ "log_file": "/var/log/samba/log.%m"
+ },
+ "shares": [
+ {
+ "comment": "All Printers",
+ "browseable": false,
+ "create_mask": "0700",
+ "name": "printers",
+ "directory_mask": "0755",
+ "read_only": true,
+ "guest_ok": false,
+ "path": "/var/spool/samba"
+ },
+ {
+ "comment": "Printer Drivers",
+ "browseable": true,
+ "create_mask": "0744",
+ "name": "print$",
+ "directory_mask": "0755",
+ "read_only": true,
+ "guest_ok": false,
+ "path": "/var/lib/samba/printers"
+ }
+ ]
+ }
+
+ >>> data_tree.shares
+ <reconfigure.items.bound.BoundCollection object at 0x23d0610>
+ >>> [_.path for _ in data_tree.shares]
+ ['/var/spool/samba', '/var/lib/samba/printers']
+
+Data trees may consist of any Python objects, but the common approach is to use :ref:`Bound data`
+
+Data trees can be manipulated as you wish::
+
+ >>> from reconfigure.items.samba import ShareData
+ >>> share = ShareData()
+ >>> share.path = '/home/user'
+ >>> share.comment = 'New share'
+ >>> data_tree.shares.append(share)
+ >>> data_tree
+ {
+ ....
+ "shares": [
+ {
+ "comment": "All Printers",
+ "browseable": false,
+ "create_mask": "0700",
+ "name": "printers",
+ "directory_mask": "0755",
+ "read_only": true,
+ "guest_ok": false,
+ "path": "/var/spool/samba"
+ },
+ {
+ "comment": "Printer Drivers",
+ "browseable": true,
+ "create_mask": "0744",
+ "name": "print$",
+ "directory_mask": "0755",
+ "read_only": true,
+ "guest_ok": false,
+ "path": "/var/lib/samba/printers"
+ },
+ {
+ "comment": "New share",
+ "browseable": true,
+ "create_mask": "0744",
+ "name": "share",
+ "directory_mask": "0755",
+ "read_only": true,
+ "guest_ok": false,
+ "path": "/home/user"
+ }
+ ]
+
+After you're done with the modifications, the data tree must be converted back to the node tree::
+
+ >>> node_tree = builder.unbuild(data_tree)
+
diff --git a/docs/source/docs/quickstart.rst b/docs/source/docs/quickstart.rst
new file mode 100644
index 0000000..a0384e6
--- /dev/null
+++ b/docs/source/docs/quickstart.rst
@@ -0,0 +1,96 @@
+Quickstart
+==========
+
+Adding lines to ``fstab``::
+
+ >>> from reconfigure.configs import FSTabConfig
+ >>> from reconfigure.items.fstab import FilesystemData
+ >>>
+ >>> config = FSTabConfig(path='/etc/fstab')
+ >>> config.load()
+ >>> print config.tree
+ {
+ "filesystems": [
+ {
+ "passno": "0",
+ "device": "proc",
+ "mountpoint": "/proc",
+ "freq": "0",
+ "type": "proc",
+ "options": "nodev,noexec,nosuid"
+ },
+ {
+ "passno": "1",
+ "device": "UUID=dfccef1e-d46c-45b8-969d-51391898c55e",
+ "mountpoint": "/",
+ "freq": "0",
+ "type": "ext4",
+ "options": "errors=remount-ro"
+ }
+ ]
+ }
+ >>> tmpfs = FilesystemData()
+ >>> tmpfs.mountpoint = '/srv/cache'
+ >>> tmpfs.type = 'tmpfs'
+ >>> tmpfs.device = 'none'
+ >>> config.tree.filesystems.append(tmpfs)
+ >>> config.save()
+ >>> quit()
+ $ cat /etc/fstab
+ proc /proc proc nodev,noexec,nosuid 0 0
+ UUID=dfccef1e-d46c-45b8-969d-51391898c55e / ext4 errors=remount-ro 0 1
+ none /srv/cache tmpfs none 0 0
+
+Changing Samba settings::
+
+ >>> from reconfigure.configs import SambaConfig
+ >>> config = SambaConfig(path='/etc/samba/smb.conf')
+ >>> config.load()
+ >>> print config.tree.shares
+ [
+ {
+ "comment": "All Printers",
+ "browseable": false,
+ "create_mask": "0700",
+ "name": "printers",
+ "directory_mask": "0755",
+ "read_only": true,
+ "guest_ok": false,
+ "path": "/var/spool/samba"
+ },
+ {
+ "comment": "Printer Drivers",
+ "browseable": true,
+ "create_mask": "0744",
+ "name": "print$",
+ "directory_mask": "0755",
+ "read_only": true,
+ "guest_ok": false,
+ "path": "/var/lib/samba/printers"
+ }
+ ]
+ >>> config.tree.shares[0].guest_ok = True
+ >>> print config.tree.shares
+ [
+ {
+ "comment": "All Printers",
+ "browseable": false,
+ "create_mask": "0700",
+ "name": "printers",
+ "directory_mask": "0755",
+ "read_only": true,
+ "guest_ok": true,
+ "path": "/var/spool/samba"
+ },
+ {
+ "comment": "Printer Drivers",
+ "browseable": true,
+ "create_mask": "0744",
+ "name": "print$",
+ "directory_mask": "0755",
+ "read_only": true,
+ "guest_ok": false,
+ "path": "/var/lib/samba/printers"
+ }
+ ]
+ >>> config.save()
diff --git a/docs/source/index.rst b/docs/source/index.rst
new file mode 100644
index 0000000..940f2bd
--- /dev/null
+++ b/docs/source/index.rst
@@ -0,0 +1,46 @@
+.. Reconfigure documentation master file, created by
+ sphinx-quickstart on Mon Aug 15 15:18:35 2011.
+ You can adapt this file completely to your liking, but it should at least
+ contain the root `toctree` directive.
+
+Welcome to Reconfigure's documentation!
+=======================================
+
+Links:
+------
+
+* `Source at GitHub <http://github.com/Eugeny/reconfigure>`_
+* Questions? `Email me <mailto:e@ajeni.org>`_
+* `PyPI <http://pypi.python.org/pypi?:action=display&name=reconfigure>`_
+* `CI <http://ajenti.org:9090/job/reconfigure/>`_
+
+Contents:
+---------
+
+.. toctree::
+ :maxdepth: 2
+
+ docs/quickstart
+ docs/architecture
+
+
+API Reference:
+--------------
+
+.. toctree::
+ :maxdepth: 2
+
+ ref/reconfigure.configs
+ ref/reconfigure.parsers
+ ref/reconfigure.nodes
+ ref/reconfigure.includers
+ ref/reconfigure.builders
+ ref/reconfigure.items.bound
+
+
+Indices and tables
+==================
+
+* :ref:`genindex`
+* :ref:`modindex`
+* :ref:`search`
diff --git a/docs/source/ref/reconfigure.builders.rst b/docs/source/ref/reconfigure.builders.rst
new file mode 100644
index 0000000..a2edb2d
--- /dev/null
+++ b/docs/source/ref/reconfigure.builders.rst
@@ -0,0 +1,6 @@
+reconfigure.builders
+********************
+
+.. automodule:: reconfigure.builders
+ :members:
+ :undoc-members:
diff --git a/docs/source/ref/reconfigure.configs.rst b/docs/source/ref/reconfigure.configs.rst
new file mode 100644
index 0000000..afa99a5
--- /dev/null
+++ b/docs/source/ref/reconfigure.configs.rst
@@ -0,0 +1,8 @@
+.. _reconfigure.configs:
+
+reconfigure.configs
+*******************
+
+.. automodule:: reconfigure.configs
+ :members:
+ :undoc-members:
diff --git a/docs/source/ref/reconfigure.includers.rst b/docs/source/ref/reconfigure.includers.rst
new file mode 100644
index 0000000..1c8fdd6
--- /dev/null
+++ b/docs/source/ref/reconfigure.includers.rst
@@ -0,0 +1,6 @@
+reconfigure.includers
+*********************
+
+.. automodule:: reconfigure.includers
+ :members:
+ :undoc-members:
diff --git a/docs/source/ref/reconfigure.items.bound.rst b/docs/source/ref/reconfigure.items.bound.rst
new file mode 100644
index 0000000..2f12552
--- /dev/null
+++ b/docs/source/ref/reconfigure.items.bound.rst
@@ -0,0 +1,6 @@
+reconfigure.items.bound
+***********************
+
+.. automodule:: reconfigure.items.bound
+ :members:
+ :undoc-members:
diff --git a/docs/source/ref/reconfigure.nodes.rst b/docs/source/ref/reconfigure.nodes.rst
new file mode 100644
index 0000000..9d522f9
--- /dev/null
+++ b/docs/source/ref/reconfigure.nodes.rst
@@ -0,0 +1,6 @@
+reconfigure.nodes
+*****************
+
+.. automodule:: reconfigure.nodes
+ :members:
+ :undoc-members:
diff --git a/docs/source/ref/reconfigure.parsers.rst b/docs/source/ref/reconfigure.parsers.rst
new file mode 100644
index 0000000..45fc310
--- /dev/null
+++ b/docs/source/ref/reconfigure.parsers.rst
@@ -0,0 +1,6 @@
+reconfigure.parsers
+*******************
+
+.. automodule:: reconfigure.parsers
+ :members:
+ :undoc-members: