Permalink
Browse files

Rewrite generators doc as Template arch

  • Loading branch information...
1 parent 1c79a15 commit 856cd1bb8441a4b027dfb56123958538a87c7c5b @lsegal committed Oct 25, 2009
Showing with 282 additions and 212 deletions.
  1. +1 −1 .yardopts
  2. +0 −211 docs/Generators.md
  3. +281 −0 docs/Templates.md
View
2 .yardopts
@@ -6,7 +6,7 @@ docs/CodeObjects.md
docs/Tags.md
docs/Parser.md
docs/Handlers.md
-docs/Generators.md
+docs/Templates.md
docs/FAQ.md
docs/Glossary.md
LICENSE
View
211 docs/Generators.md
@@ -1,211 +0,0 @@
-Generators Architecture
-=======================
-
-Note: This document describes the architecture of the current generators
-implementation which is likely to undergo large changes in the 0.2.4
-release. Keep this in mind if you plan on extending or implementing
-custom generators.
-
-Generators are the main component in the output generation process of YARD,
-which is invoked when conventional HTML/text output needs to be generated
-for a set of code objects.
-
-Design Goals
-------------
-
-The general design attempts to be as abstracted from actual content and templates
-as possible. Unlike RDoc which uses one file to describe the entire template,
-YARD splits up the generation of code objects into small components, allowing
-template modification for smaller subsets of a full template without having to
-duplicate the entire template itself. This is necessary because of YARD's support
-for plugins. YARD is designed for extensibility by external plugins, and because
-of this, no one plugin can be responsible for the entire template because no
-one plugin knows about the other plugins being used. For instance, if an RSpec
-plugin was added to support and document specifications in class templates,
-this information would need to be transparently added to the template to work
-in conjunction with any other plugin that performed similar template modifications.
-The design goals can be summarized as follows:
-
- 1. Output should be able to be generated for any arbitrary format with little
- modification to YARD's source code. The addition of extra templates should
- be sufficient.
- 2. The output generated for an object should independently generated data
- from arbitrary sources. These independent components are called "sections".
- 3. Sections should be able to be inserted into any object without affecting
- any existing sections in the document. This allows for easy modification
- of templates by plugins.
-
-Generators
-----------
-
-Generator classes are the objects used to orchestrate the design goals listed
-above. Specifically, they organize the sections and render the template contents
-depending on the format. The main method used to initiate output is the
-{YARD::Generators::Base#generate #generate} method which takes a list of
-objects to generate output for. A good example of this is the FullDocGenerator,
-which generates conventional HTML documentation:
-
- # all_objects is an array of module and class objects
- Generators::FullDocGenerator.new(options).generate(all_objects)
-
-Generator Options
------------------
-
-A generator keeps state when it is generating output. This state is kept in
-an options hash which is initially passed to it during instantiation. Some
-default options set the template style (`:template`), the output format (`:format`),
-and the serializer to use (`:serializer`). For example, initializing the
-{YARD::Generators::QuickDocGenerator} to output as text instead of HTML can be
-done as follows:
-
- YARD::Generators::QuickDocGenerator.new(:format => :text).generate(objects)
-
-Serializer
-----------
-
-This class abstracts the logic involved in deciding how to serialize data to
-the expected endpoint. For instance, there is both a {YARD::Serializers::StdoutSerializer StdoutSerializer}
-and {YARD::Serializers::FileSystemSerializer FileSystemSerializer} class for
-outputting to console or to a file respectively. When endpoints with locations
-are used (like files or URLs), the serializer implements the {YARD::Serializers::Base#serialized_path #serialized_path}
-method. This allows the translation from a code object to its path at the endpoint,
-which enables inter-document linking.
-
-Generated objects are automatically serialized using the object if present,
-otherwise the generated object is returned as a string to its parent. Nested
-generator objects automatically set the serializer to nil so that they return
-as a String to their parent.
-
-Templates
----------
-
-Templates for a generator are by default found inside the one of the template
-root paths (there can be multiple template paths). A standard template
-directory looks like the following tree:
-
- (Assuming templates/ is a template root path)
- templates/
- |-- default
- | |-- attributes
- | | |-- html
- | | | `-- header.erb
- | | `-- text
- | | `-- header.erb
- | |-- class
- | | `-- html
- | | `-- header.erb
- | |-- constants
- | | `-- html
- | | |-- constants.erb
- | | |-- header.erb
- | | |-- included.erb
- | | `-- inherited.erb
- ...
-
-The path `default` refers to the template style and the directories at the next
-level (such as `attributes`) refer to templates for a generator. The next directory
-refers to the output format being used defined by the `:format` generator option.
-As we saw in the above example, the format option can be set to `:text`, which
-would use the `text/` directory instead of `html/`. Finally, the individual .erb
-files are the sections that make up the generator.
-
-Sections
---------
-
-As mentioned above, sections are smaller components that correlate to template
-fragments. Practically speaking, a section can either be a template fragment
-(a conventional .erb file or other supported templating language), a method
-(which returns a String) or another Generator object (which in turn has its own
-list of sections).
-
-Creating a Generator
---------------------
-
-To create a generator, subclass {YARD::Generators::Base} and implement the
-`#sections_for(object)` method. This method should return a list of sections where
-a Symbol refers to a method or template name and a class refers to a generator.
-
- def sections_for(object)
- case object
- when MethodObject
- [:main, [G(AnotherGenerator)], :footer]
- else
- []
- end
- end
-
-A few points about the above example:
-
- * The method can return different lists depending on the object.
- * The list of objects is not flat, we will see how nested lists can be used
- in a future example.
- * The convenience method `G()` instantiates a generator out of the class using
- the existing options.
-
-If a section is a Symbol, the generator first checks if a method is defined
-with that name, otherwise it checks in the template directories. If a method
-by the symbol name is defined, you need to manually call {YARD::Generators::Base#render #render}
-to return the contents of the template.
-
-Nested Sections
----------------
-
-Sections often require the ability to encapsulate a set of sub-sections in markup
-(HTML, for instance). Rather than use heavier Generator subclass objects, a more
-lightweight solution is to nest a set of sub-sections as a list that follows
-a section, for example:
-
- def sections_for(object)
- [:header, [:section_a, :section_b]]
- end
-
-The above example nests `section_a` and `section_b` within the `header` section.
-Practically speaking, these sections can be placed in the result by `yield`ing
-to them. A sample header.erb template might contain:
-
- <h2>Header</h2>
- <div id="contents">
- <%= yield %>
- </div>
-
-This template code would place the output of `section_a` and `section_b` within
-the above div element. Using yield, we can also change the object that is being
-generated. For example, we may want to yield the first method of the class.
-We can do this like so:
-
- <h2>First method</h2>
- <%= yield(current_object.meths.first) %>
-
-This would run the nested sections for the method object instead of the class.
-
-Before Filters
---------------
-
-Generators can run before filters using the {YARD::Generators::Base.before_section before_section} method
-for all or a specific section to test if the section should be generated or
-skipped. For instance, we can do the following to generate the section :foo only
-for MethodObjects:
-
- class MyGenerator < YARD::Generators::Base
- before_section :foo, :is_method?
-
- def sections_for(object)
- [:foo, :bar, :baz]
- end
-
- private
-
- def is_method?(object)
- object.is_a?(MethodObject)
- end
- end
-
-Without the argument `:foo`, the before filter would be applied to all sections.
-Note that we must return `false` to skip a section. A return type of nil is not
-enough to skip the section.
-
-There is also a {YARD::Generators::Base.before_list before_list} method to run
-a filter before the entire generator is run. This is useful for doing necessary
-filesystem setup or for generating assets (stylesheets) before generating output
-for the objects. Note that in this case you will need to serialize your data directly
-using the serializer object (described above).
View
281 docs/Templates.md
@@ -0,0 +1,281 @@
+Templates Architecture
+======================
+
+Templates are the main component in the output rendering process of YARD,
+which is invoked when conventional HTML/text output needs to be rendered
+for a set of code objects.
+
+Design Goals
+------------
+
+The general design attempts to be as abstracted from actual content and templates
+as possible. Unlike RDoc which uses one file to describe the entire template,
+YARD splits up the rendering of code objects into small components, allowing
+template modification for smaller subsets of a full template without having to
+duplicate the entire template itself. This is necessary because of YARD's support
+for plugins. YARD is designed for extensibility by external plugins, and because
+of this, no one plugin can be responsible for the entire template because no
+one plugin knows about the other plugins being used. For instance, if an RSpec
+plugin was added to support and document specifications in class templates,
+this information would need to be transparently added to the template to work
+in conjunction with any other plugin that performed similar template modifications.
+The design goals can be summarized as follows:
+
+ 1. Output should be able to be rendered for any arbitrary format with little
+ modification to YARD's source code. The addition of extra templates should
+ be sufficient.
+ 2. The output rendered for an object should independently rendered data
+ from arbitrary sources. These independent components are called "sections".
+ 3. Sections should be able to be inserted into any object without affecting
+ any existing sections in the document. This allows for easy modification
+ of templates by plugins.
+
+Templates
+---------
+
+Template modules are the objects used to orchestrate the design goals listed
+above. Specifically, they organize the sections and render the template contents
+depending on the format.
+
+Engine
+------
+
+The Engine class orchestrates the creation and rendering of Template modules and
+handles serialization or specific rendering scenarios (like HTML). To create
+a template, use the {YARD::Templates::Engine.template template} method. The two most
+common methods used to initiate output are the {YARD::Templates::Engine.render render}
+and {YARD::Templates::Engine.generate generate} methods which generate and
+optionally serialize output to a file. The latter, `#generate`, is used
+specially to generate HTML documentation and copy over assets that may be
+needed. For instance, an object may be rendered with:
+
+ YARD::Templates::Engine.render(:object => myobject)
+
+A set of objects may be rendered into HTML documentation by using:
+
+ # all_objects is an array of module and class objects
+ # options includes a :serializer key to copy output to the file system
+ YARD::Templates::Engine.generate(all_objects, options)
+
+Note that these methods should not be called directly. The {YARD::CodeObjects::Base}
+class has a {YARD::CodeObjects::Base#format #format} helper method to render an
+object. For instance, the above render example is equivalent to the simple
+call `myobject.format`. The `generate` method is a special kind of render
+and is called from the {YARD::CLI::Yardoc} command line utility.
+
+Template Options
+----------------
+
+A template keeps state when it is rendering output. This state is kept in
+an options hash which is initially passed to it during instantiation. Some
+default options set the template style (`:template`), the output format (`:format`),
+and the serializer to use (`:serializer`). This options hash is modifiable
+from all methods seen above. For example, initializing a template to output as
+HTML instead of text can be done as follows:
+
+ myobject.format(:format => :html)
+
+Serializer
+----------
+
+This class abstracts the logic involved in deciding how to serialize data to
+the expected endpoint. For instance, there is both a {YARD::Serializers::StdoutSerializer StdoutSerializer}
+and {YARD::Serializers::FileSystemSerializer FileSystemSerializer} class for
+outputting to console or to a file respectively. When endpoints with locations
+are used (like files or URLs), the serializer implements the {YARD::Serializers::Base#serialized_path #serialized_path}
+method. This allows the translation from a code object to its path at the endpoint,
+which enables inter-document linking.
+
+Rendered objects are automatically serialized using the object if present,
+otherwise the rendered object is returned as a string to its parent. Nested
+Templates automatically set the serializer to nil so that they return
+as a String to their parent.
+
+Creating a Template
+-------------------
+
+Templates are represented by a directory inside the {YARD::Templates::Engine.template_paths}
+on disk. A standard template directory looks like the following tree:
+
+ (Assuming templates/ is a template path)
+ templates
+ `-- default
+ |-- class
+ | |-- dot
+ | | |-- setup.rb
+ | | `-- superklass.erb
+ | |-- html
+ | | |-- constructor_details.erb
+ | | |-- setup.rb
+ | | `-- subclasses.erb
+ | |-- setup.rb
+ | `-- text
+ | |-- setup.rb
+ | `-- subclasses.erb
+ |-- docstring
+ | |-- html
+ | | |-- abstract.erb
+ | | |-- deprecated.erb
+ | | |-- index.erb
+ | | `-- text.erb
+ | |-- setup.rb
+ | `-- text
+ | |-- abstract.erb
+ | |-- deprecated.erb
+ | |-- index.erb
+ | `-- text.erb
+
+The path `default` refers to the template style (:template key in options hash)
+and the directories at the next level (such as `class`) refer to template
+`:type` (options hash key) for a template. The next directory refers to the
+output format being used defined by the `:format` template option.
+
+As we saw in the above example, the format option can be set to `:html`, which
+would use the `html/` directory instead of `text/`. Finally, the individual .erb
+files are the sections that make up the template.
+
+Note that the subdirectory `html/` is also its own "template" that inherits
+from the parent directory. We will see more on this later.
+
+setup.rb
+--------
+
+Every template should have at least one `setup.rb` file that defines the
+{YARD::Templates::Template#init #init} method to set the
+{YARD::Templates::Template#sections #sections} used by the template. If
+a setup.rb is not defined in the template itself, there should be a template
+that is inherited (via parent directory or explcitly) that sets the sections
+on a newly created template.
+
+A standard setup.rb file looks like:
+
+ def init
+ sections :section1, :section2, :section3
+ end
+
+Sections
+--------
+
+Sections are smaller components that correlate to template
+fragments. Practically speaking, a section can either be a template fragment
+(a conventional .erb file or other supported templating language), a method
+(which returns a String) or another {YARD::Templates::Template} (which in turn has its own
+list of sections).
+
+Nested Sections
+---------------
+
+Sections often require the ability to encapsulate a set of sub-sections in markup
+(HTML, for instance). Rather than use heavier Template subclass objects, a more
+lightweight solution is to nest a set of sub-sections as a list that follows
+a section, for example:
+
+ def sections_for(object)
+ [:header, [:section_a, :section_b]]
+ end
+
+The above example nests `section_a` and `section_b` within the `header` section.
+Practically speaking, these sections can be placed in the result by `yield`ing
+to them. A sample header.erb template might contain:
+
+ &lt;h2&gt;Header&lt;/h2&gt;
+ &lt;div id=&quot;contents&quot;&gt;
+ &lt;%= yield %&gt;
+ &lt;/div&gt;
+
+This template code would place the output of `section_a` and `section_b` within
+the above div element. Using yield, we can also change the object that is being
+rendered. For example, we may want to yield the first method of the class.
+We can do this like so:
+
+ &lt;h2&gt;First method&lt;/h2&gt;
+ &lt;%= yield(current_object.meths.first) %&gt;
+
+This would run the nested sections for the method object instead of the class.
+
+Inheriting Templates
+--------------------
+
+Parent directory templates are automatically inherited (or mixed in, to be
+more accurate) by the current template. This means that the 'default/class/html'
+template automatically inherits from 'default/class'. This also means that anything
+defined in 'default/class/setup.rb' can be overridden by 'default/class/html/setup.rb'.
+
+Since the Template module is a module, and not a class, they can be mixed in
+explicitly (via include/extend) from other templates, which allows templates
+to share erb files or helper logic. The 'default/class' template explicitly
+mixes in the 'default/module' template, since it uses much of the same sections.
+This is done with the helper {YARD::Templates::Template::ClassMethods#T T} method, which
+is simply a shorthand for {YARD::Templates::Engine.template Engine.template}.
+It can then override (using standard inheritance) the sections from the module
+template and insert sections pertaining to classes. This is one of the design
+goals described above.
+
+For instance, the first line in `default/class/html/setup.rb` is:
+
+ include T('default/module/html')
+
+This includes the 'default/module/html', which means it also includes 'default/module'
+by extension. This allows class to make use of any of module's erb files.
+
+Inserting Sections
+------------------
+
+The ability to insert sections was mentioned above. The class template, for
+instance, will modify the #init method to insert class specific sections:
+
+ def init
+ super
+ sections.place(:subclasses).before(:children)
+ sections.delete(:children)
+ sections.place([:constructor_details, [T('method_details')]]).before(:methodmissing)
+ end
+
+Observe how sections has been modified after the super method was called (the
+super method would have been defined in `default/module/setup.rb`). The
+custom method {Array#place} is added by YARD to allow sections to be inserted
+before or after another section by it's given name rather than index. This
+allows the overriding of templates in a way that does not depend on where
+the section is located (since it may have been overriden by another module).
+
+Overriding Templates by Registering a Template Path
+---------------------------------------------------
+
+Inheriting templates explicitly is useful when creating a customized template
+that wants to take advantage of code re-use. However, most users who want
+to customize YARD templates will want to override existing behaviour without
+creating a template from scratch.
+
+YARD solves this problem by allowing other template paths to be registered.
+Because template modules are represented by a relative path such as 'default/class',
+they can be found within any of the registered template paths. A new template
+path is registered as:
+
+ YARD::Templates::Engine.register_template_path '/path/to/mytemplates'
+
+At this point, any time the 'default/class' template is loaded, the template
+will first be looked for inside the newly registered template path. If found,
+it will be used as the template module, with the modules from the other
+template paths implicitly mixed in.
+
+Therefore, by using the same directory structure as a builtin YARD template,
+a user can customize or override individual templates as if the old ones were
+inherited. A real world example would further modify the 'default/class' template
+seen above by creating such a path in our '/path/to/mytemplates' custom template
+path:
+
+ /path/to/mytemplates/:
+ |-- class
+ | |-- html
+ | | |-- customsection.erb
+ | |-- setup.rb
+
+The `setup.rb` file would look like:
+
+ def init
+ super
+ sections.push :customsection
+ end
+
+Now, when a class object is formatted as HTML, our customsection.erb will be
+appended to the rendered data.

0 comments on commit 856cd1b

Please sign in to comment.