Merge pull request #7 from hkenney/PDOC-3_refactor-prototype-code

(PDOC-3) Refactor prototype code to make it more readable
This commit is contained in:
Andrew Parker 2014-09-30 14:57:35 -07:00
commit 970b60481f
25 changed files with 539 additions and 551 deletions

View File

@ -3,6 +3,7 @@ require 'puppet/face'
Puppet::Face.define(:strings, '0.0.1') do
summary "Generate Puppet documentation with YARD."
# Ensures that the user has the needed features to use puppet strings
def check_required_features
unless Puppet.features.yard?
raise RuntimeError, "The 'yard' gem must be installed in order to use this face."
@ -17,14 +18,6 @@ Puppet::Face.define(:strings, '0.0.1') do
end
end
# Maps things like the Puppet `--debug` flag to YARD options.
def merge_puppet_args!(yard_args)
yard_args.unshift '--debug' if Puppet[:debug]
yard_args.unshift '--backtrace' if Puppet[:trace]
yard_args
end
# A list of globs that generates the default list of module files from which
# documentation can be extracted.
#
@ -40,6 +33,9 @@ Puppet::Face.define(:strings, '0.0.1') do
when_invoked do |*args|
check_required_features
require 'puppetx/puppetlabs/strings/actions'
yardoc_actions = Puppetx::PuppetLabs::Strings::Actions.new(Puppet[:debug], Puppet[:trace])
# The last element of the argument array should be the options hash.
#
@ -52,12 +48,8 @@ Puppet::Face.define(:strings, '0.0.1') do
# For now, assume the remaining positional args are a list of manifest
# and ruby files to parse.
yard_args = (args.empty? ? MODULE_SOURCEFILES : args)
merge_puppet_args!(yard_args)
require 'puppetx/puppetlabs/strings/yard/plugin'
# Hand off to YARD for further processing.
YARD::CLI::Yardoc.run(*yard_args)
yardoc_actions.generate_documentation(*yard_args)
end
end
@ -65,59 +57,29 @@ Puppet::Face.define(:strings, '0.0.1') do
# (.yardoc directories) for Ruby Gems. Currently lacks the fine-grained
# control over where these indicies are created and just dumps them in the
# module roots.
action(:modules) do
summary "Generate YARD indices for a list of modules."
arguments "[module-name ...]"
when_invoked do |*args|
check_required_features
require 'puppetx/puppetlabs/strings/yard/plugin'
opts = args.pop
# NOTE: The retrun value of the `module` Face seems to have changed in
# 3.6.x. This part of the code will blow up if run under an earlier
# version of Puppet.
modules = Puppet::Face[:module, :current].list
module_list = modules[:modules_by_path].values.flatten
# TODO: Can use select! if Ruby 1.8.7 support is dropped.
module_list = module_list.select {|m| args.include? m.name} unless args.empty?
# Invoke `yardoc` with -n so that it doesn't generate any HTML output but
# does build a `.yardoc` index that other tools can generate output from.
yard_args = %w[--no-stats -n] + MODULE_SOURCEFILES
merge_puppet_args!(yard_args)
module_list.each do |m|
Dir.chdir(m.path) do
YARD::CLI::Yardoc.run(*yard_args)
# Cear the global Registry so that objects from one module don't
# bleed into the next.
YARD::Registry.clear
end
end
end
end
action(:server) do
summary "Serve YARD documentation for modules."
when_invoked do |*args|
check_required_features
require 'puppetx/puppetlabs/strings/yard/plugin'
require 'puppetx/puppetlabs/strings/actions'
server_actions = Puppetx::PuppetLabs::Strings::Actions.new(Puppet[:debug], Puppet[:trace])
opts = args.pop
module_names = args
# FIXME: This is pretty inefficient as it forcibly re-generates the YARD
# indicies each time the server is started. However, it ensures things are
# generated properly.
module_list = Puppet::Face[:strings, :current].modules
module_list = server_actions.index_documentation_for_modules(module_names, MODULE_SOURCEFILES)
module_tuples = module_list.map do |mod|
name = (mod.forge_name || mod.name).gsub('/', '-')
yard_index = File.join(mod.path, '.yardoc')
module_tuples = server_actions.generate_module_tuples(module_list)
[name, yard_index]
module_tuples.map! do |mod|
[mod[:name], mod[:index_path]]
end
# The `-m` flag means a list of name/path pairs will follow. The name is
@ -126,7 +88,8 @@ Puppet::Face.define(:strings, '0.0.1') do
yard_args = %w[-m -q] + module_tuples.flatten
merge_puppet_args!(yard_args)
YARD::CLI::Server.run(*yard_args)
server_actions.serve_documentation(*yard_args)
end
end
end

View File

@ -1,4 +0,0 @@
require 'puppet/util/feature'
# Support require_relative under Ruby 1.8.7.
Puppet.features.add(:require_relative, :libs => ['backports/1.9.1/kernel/require_relative'])

View File

@ -1,16 +1,53 @@
require 'puppet'
require 'puppetx'
require 'puppet/pops'
require 'puppet/util/docs'
require 'yard'
# Nothing to see here except forward declarations.
module Puppetx::PuppetLabs
module Strings
# This submodule contains bits that interface with the YARD plugin system.
module YARD
end
# This submodule contains bits that operate on the Pops module produced by
# the Future parser.
module Pops
require 'puppetx/puppetlabs/strings/pops/yard_statement'
require 'puppetx/puppetlabs/strings/pops/yard_transformer'
end
# This submodule contains bits that interface with the YARD plugin system.
module YARD
require 'puppetx/puppetlabs/strings/yard/monkey_patches'
require 'puppetx/puppetlabs/strings/yard/parser'
# This submodule contains code objects which are used to represent relevant
# aspects of puppet code in YARD's Registry
module CodeObjects
require 'puppetx/puppetlabs/strings/yard/code_objects/puppet_namespace_object'
require 'puppetx/puppetlabs/strings/yard/code_objects/defined_type_object'
require 'puppetx/puppetlabs/strings/yard/code_objects/host_class_object'
end
# This submodule contains handlers which are used to extract relevant data about
# puppet code from the ASTs produced by the Ruby and Puppet parsers
module Handlers
# This utility library contains some tools for working with Puppet docstrings
require 'puppetx/puppetlabs/strings/yard/handlers/base'
require 'puppetx/puppetlabs/strings/yard/handlers/defined_type_handler'
require 'puppetx/puppetlabs/strings/yard/handlers/host_class_handler'
require 'puppetx/puppetlabs/strings/yard/handlers/puppet_3x_function_handler'
require 'puppetx/puppetlabs/strings/yard/handlers/puppet_4x_function_handler'
end
::YARD::Parser::SourceParser.register_parser_type(:puppet,
Puppetx::PuppetLabs::Strings::YARD::PuppetParser,
['pp'])
::YARD::Handlers::Processor.register_handler_namespace(:puppet,
Puppetx::PuppetLabs::Strings::YARD::Handlers)
# FIXME: Might not be the best idea to have the template code on the Ruby
# LOAD_PATH as the contents of this directory really aren't library code.
::YARD::Templates::Engine.register_template_path(
File.join(File.dirname(__FILE__), 'strings', 'yard', 'templates'))
end
end
end

View File

@ -0,0 +1,92 @@
require 'puppetx/puppetlabs/strings'
class Puppetx::PuppetLabs::Strings::Actions
# Creates a new instance of the Actions class by determining
# whether or not debug and backtrace arguments should be sent
# to YARD
def initialize(puppet_debug, puppet_backtrace)
@debug = puppet_debug
@backtrace = puppet_backtrace
end
# Holds the name of a module and the file path to its YARD index
ModuleIndex = Struct.new(:name, :index_path)
# Maps things like the Puppet `--debug` flag to YARD options.
def merge_puppet_args!(yard_args)
yard_args.unshift '--debug' if @debug
yard_args.unshift '--backtrace' if @backtrace
yard_args
end
# Builds doc indices (.yardoc directories) for modules.
# Currently lacks the fine-grained control over where these
# indices are created and just dumps them in the module roots.
#
# @return [Array<Module>] the modules to be documented
#
# @param [Array<String>] module_names a list of the module source files
# @param [Array<String>] module_sourcefiles default list of module files
def index_documentation_for_modules(module_names, module_sourcefiles)
# NOTE: The return value of the `module` Face seems to have changed in
# 3.6.x. This part of the code will blow up if run under an earlier
# version of Puppet.
modules = Puppet::Face[:module, :current].list
module_list = modules[:modules_by_path].values.flatten
module_list.select! {|m| module_names.include? m.name} unless module_names.empty?
# Invoke `yardoc` with -n so that it doesn't generate any HTML output but
# does build a `.yardoc` index that other tools can generate output from.
yard_args = %w[--no-stats -n] + module_sourcefiles
merge_puppet_args!(yard_args)
module_list.each do |m|
Dir.chdir(m.path) do
YARD::CLI::Yardoc.run(*yard_args)
# Clear the global Registry so that objects from one module don't
# bleed into the next.
YARD::Registry.clear
end
end
end
# Extracts the needed information of the modules we're documenting
#
# @return [Array<ModuleIndex>] An array of representation of the modules
# to produce documentation for. Each ModuleIndex contains the module name
# and the path to its YARD index
#
# @param [Array<String>] module_list a list of the module source files
def generate_module_tuples(module_list)
module_list.map do |mod|
name = (mod.forge_name || mod.name).gsub('/', '-')
yard_index = File.join(mod.path, '.yardoc')
ModuleIndex.new(name, yard_index)
end
end
# Hands off the needed information to YARD so it may
# serve the documentation
#
# @param [Array<String>] yard_args a list of all the arguments to pass to YARD
def serve_documentation(*yard_args)
merge_puppet_args!(yard_args)
YARD::CLI::Server.run(*yard_args)
end
# Hands off the needed information to YARD so it may
# generate the documentation
#
# @param [Array<String>] yard_args a list of all the arguments to pass to YARD
def generate_documentation(*yard_args)
merge_puppet_args!(yard_args)
YARD::CLI::Yardoc.run(*yard_args)
end
end

View File

@ -1,16 +1,12 @@
require 'ostruct'
require 'puppet/pops'
require_relative '../../strings'
module Puppetx::PuppetLabs::Strings::Pops
# An adapter class that conforms a Pops model instance + adapters to the
# interface expected by YARD handlers.
#
# FIXME: Inhertiting from OpenStruct is a bit of a hack. It allows attributes
# to be declared as needed but in the long run understandibility of the code
# would be improved by having a concrete model.
class YARDStatement < OpenStruct
class Puppetx::PuppetLabs::Strings::Pops::YARDStatement < OpenStruct
attr_reader :pops_obj, :comments
def initialize(pops_obj)
@ -73,6 +69,4 @@ module Puppetx::PuppetLabs::Strings::Pops
# Stick everything back together.
comments.join
end
end
end

View File

@ -1,16 +1,10 @@
require 'puppet/pops'
require_relative '../../strings'
require_relative 'yard_statement'
module Puppetx::PuppetLabs::Strings::Pops
# Loosely based on the TreeDumper classes in Pops::Model. The responsibility of
# this class is to walk a Pops::Model and output objects that can be consumed
# by YARD handlers.
#
# @note Currently, this class only extracts node, host class and type
# definitions.
class YARDTransformer
class Puppetx::PuppetLabs::Strings::Pops::YARDTransformer
def initialize
@transform_visitor = Puppet::Pops::Visitor.new(self, 'transform')
end
@ -32,7 +26,7 @@ module Puppetx::PuppetLabs::Strings::Pops
# Extract comments from type definitions and class definitions. Wrap them
# into YARDStatement objects that provide an interface for YARD handlers.
def transform_NamedDefinition(o)
obj = YARDStatement.new(o)
obj = Puppetx::PuppetLabs::Strings::Pops::YARDStatement.new(o)
obj.parameters = o.parameters.map do |p|
param_tuple = [transform(p)]
param_tuple << ( p.value.nil? ? nil : transform(p.value) )
@ -43,7 +37,7 @@ module Puppetx::PuppetLabs::Strings::Pops
# Catch-all visitor.
def transform_Positioned(o)
YARDStatement.new(o)
Puppetx::PuppetLabs::Strings::Pops::YARDStatement.new(o)
end
# nil in... nil out!
@ -51,4 +45,3 @@ module Puppetx::PuppetLabs::Strings::Pops
nil
end
end
end

View File

@ -1,3 +0,0 @@
require_relative 'code_objects/puppet_namespace_object'
require_relative 'code_objects/defined_type_object'
require_relative 'code_objects/host_class_object'

View File

@ -1,11 +1,5 @@
require 'puppet/pops'
require_relative 'puppet_namespace_object'
module Puppetx::PuppetLabs::Strings::YARD::CodeObjects
class DefinedTypeObject < PuppetNamespaceObject
class Puppetx::PuppetLabs::Strings::YARD::CodeObjects::DefinedTypeObject < Puppetx::PuppetLabs::Strings::YARD::CodeObjects::PuppetNamespaceObject
# A list of parameters attached to this class.
# @return [Array<Array(String, String)>]
attr_accessor :parameters
end
end

View File

@ -1,7 +1,4 @@
require_relative 'defined_type_object'
module Puppetx::PuppetLabs::Strings::YARD::CodeObjects
class HostClassObject < DefinedTypeObject
class Puppetx::PuppetLabs::Strings::YARD::CodeObjects::HostClassObject < Puppetx::PuppetLabs::Strings::YARD::CodeObjects::DefinedTypeObject
# The {HostClassObject} that this class inherits from, if any.
# @return [HostClassObject, Proxy, nil]
attr_accessor :parent_class
@ -9,7 +6,7 @@ module Puppetx::PuppetLabs::Strings::YARD::CodeObjects
# NOTE: `include_mods` is never used as it makes no sense for Puppet, but
# this is called by `YARD::Registry` and it will pass a parameter.
def inheritance_tree(include_mods = false)
if parent_class.is_a?(HostClassObject)
if parent_class.is_a?(Puppetx::PuppetLabs::Strings::YARD::CodeObjects::HostClassObject)
# Cool. We got a host class. Return self + parent inheritance tree.
[self] + parent_class.inheritance_tree
elsif parent_class.is_a?(YARD::CodeObjects::Proxy)
@ -23,4 +20,3 @@ module Puppetx::PuppetLabs::Strings::YARD::CodeObjects
end
end
end
end

View File

@ -1,9 +1,4 @@
require 'yard'
require_relative '../../../strings'
module Puppetx::PuppetLabs::Strings::YARD::CodeObjects
class PuppetNamespaceObject < YARD::CodeObjects::NamespaceObject
class Puppetx::PuppetLabs::Strings::YARD::CodeObjects::PuppetNamespaceObject < YARD::CodeObjects::NamespaceObject
# NOTE: `YARD::Registry#resolve` requires a method with this signature to
# be present on all subclasses of `NamespaceObject`.
def inheritance_tree(include_mods = false)
@ -32,5 +27,4 @@ module Puppetx::PuppetLabs::Strings::YARD::CodeObjects
#
# def self.new(namespace, name, *args, &block)
end
end

View File

@ -1,6 +0,0 @@
require_relative 'handlers/base'
require_relative 'handlers/defined_type_handler'
require_relative 'handlers/host_class_handler'
require_relative 'handlers/puppet_3x_function_handler'
require_relative 'handlers/puppet_4x_function_handler'

View File

@ -1,11 +1,4 @@
require 'yard'
require 'puppet/pops'
require_relative '../../../strings'
require_relative '../code_objects'
module Puppetx::PuppetLabs::Strings::YARD::Handlers
class Base < YARD::Handlers::Base
class Puppetx::PuppetLabs::Strings::YARD::Handlers::Base < YARD::Handlers::Base
# Easy access to Pops model objects for handler matching.
include Puppet::Pops::Model
# Easy access to custom code objects from which documentation is generated.
@ -16,4 +9,3 @@ module Puppetx::PuppetLabs::Strings::YARD::Handlers
end
end
end

View File

@ -1,7 +1,4 @@
require_relative 'base'
module Puppetx::PuppetLabs::Strings::YARD::Handlers
class DefinedTypeHandler < Base
class Puppetx::PuppetLabs::Strings::YARD::Handlers::DefinedTypeHandler < Puppetx::PuppetLabs::Strings::YARD::Handlers:: Base
handles ResourceTypeDefinition
process do
@ -15,4 +12,3 @@ module Puppetx::PuppetLabs::Strings::YARD::Handlers
register obj
end
end
end

View File

@ -1,7 +1,4 @@
require_relative 'base'
module Puppetx::PuppetLabs::Strings::YARD::Handlers
class HostClassHandler < Base
class Puppetx::PuppetLabs::Strings::YARD::Handlers::HostClassHandler < Puppetx::PuppetLabs::Strings::YARD::Handlers::Base
handles HostClassDefinition
process do
@ -21,4 +18,3 @@ module Puppetx::PuppetLabs::Strings::YARD::Handlers
register obj
end
end
end

View File

@ -1,10 +1,4 @@
# This utility library contains some tools for working with Puppet docstrings.
require 'puppet/util/docs'
require_relative '../code_objects'
module Puppetx::PuppetLabs::Strings::YARD::Handlers
class Puppet3xFunctionHandler < YARD::Handlers::Ruby::Base
class Puppetx::PuppetLabs::Strings::YARD::Handlers::Puppet3xFunctionHandler < YARD::Handlers::Ruby::Base
include Puppetx::PuppetLabs::Strings::YARD::CodeObjects
handles method_call(:newfunction)
@ -121,4 +115,3 @@ module Puppetx::PuppetLabs::Strings::YARD::Handlers
Puppet::Util::Docs.scrub(source.join)
end
end
end

View File

@ -1,10 +1,7 @@
require_relative '../code_objects'
module Puppetx::PuppetLabs::Strings::YARD::Handlers
# Handles `dispatch` calls within a future parser function declaration. For
# now, it just treats any docstring as an `@overlaod` tag and attaches the
# overload to the parent function.
class Puppet4xFunctionHandler < YARD::Handlers::Ruby::Base
class Puppetx::PuppetLabs::Strings::YARD::Handlers::Puppet4xFunctionHandler < YARD::Handlers::Ruby::Base
include Puppetx::PuppetLabs::Strings::YARD::CodeObjects
handles method_call(:dispatch)
@ -130,4 +127,3 @@ module Puppetx::PuppetLabs::Strings::YARD::Handlers
Puppet::Util::Docs.scrub(source.join)
end
end
end

View File

@ -1,11 +1,10 @@
require 'yard'
require 'puppet/pops'
require_relative '../../strings'
require_relative '../pops/yard_transformer'
require 'puppetx/puppetlabs/strings'
require 'puppetx/puppetlabs/strings//pops/yard_transformer'
module Puppetx::PuppetLabs::Strings::YARD
class PuppetParser < YARD::Parser::Base
class Puppetx::PuppetLabs::Strings::YARD::PuppetParser < YARD::Parser::Base
attr_reader :file, :source
def initialize(source, filename)
@ -29,4 +28,3 @@ module Puppetx::PuppetLabs::Strings::YARD
end
end
end

View File

@ -1,22 +0,0 @@
# TODO: Decide if supporting 1.8.7 is really worth it.
if RUBY_VERSION < '1.9'
require 'backports/1.9.1/kernel/require_relative'
end
require 'puppet'
require_relative 'monkey_patches'
require_relative 'parser'
require_relative 'handlers'
YARD::Parser::SourceParser.register_parser_type(:puppet,
Puppetx::PuppetLabs::Strings::YARD::PuppetParser,
['pp'])
YARD::Handlers::Processor.register_handler_namespace(:puppet,
Puppetx::PuppetLabs::Strings::YARD::Handlers)
# FIXME: Might not be the best idea to have the template code on the Ruby
# LOAD_PATH as the contents of this directory really aren't library code.
YARD::Templates::Engine.register_template_path(File.join(
File.dirname(__FILE__),
'templates'))

View File

@ -3,11 +3,17 @@ require 'spec_helper'
module StringsSpec
module Parsing
# Cleans up the Registry and gives YARD some source code
# to generate documentation for
def parse(string, parser = :ruby)
Registry.clear
YARD::Registry.clear
YARD::Parser::SourceParser.parse_string(string, parser)
end
# A custom matcher that allows us to compare aspects of a
# Code Objects to the specified values. This gives us a
# simplified way to ensure that the Code Object added to the
# Registry is what we expect when testing handlers
RSpec::Matchers.define :document_a do |arguments|
match do |actual|
compare_values(actual).empty?

View File

@ -5,9 +5,7 @@ require 'mocha'
require 'puppet'
require 'rspec'
# This is neeeded so we can access a Registry if YARD creates one
require 'puppetx/puppetlabs/strings/yard/plugin'
include YARD
require 'puppetx/puppetlabs/strings'
RSpec.configure do |config|
config.mock_with :mocha

View File

@ -67,22 +67,6 @@ describe Puppet::Face do
end
end
describe "modules action" do
it "should raise an error if yard is absent" do
Puppet.features.stubs(:yard?).returns(false)
expect{Puppet::Face[:strings, :current].modules}.to raise_error(RuntimeError, "The 'yard' gem must be installed in order to use this face.")
end
it "should raise an error if rgen is absent" do
Puppet.features.stubs(:rgen?).returns(false)
expect{Puppet::Face[:strings, :current].modules}.to raise_error(RuntimeError, "The 'rgen' gem must be installed in order to use this face.")
end
it "should raise an error if the Ruby version is less than 1.9", :if => RUBY_VERSION.match(/^1\.8/) do
expect{Puppet::Face[:strings, :current].modules}.to raise_error(RuntimeError, "This face requires Ruby 1.9 or greater.")
end
end
describe "server action" do
it "should raise an error if yard is absent" do
Puppet.features.stubs(:yard?).returns(false)
@ -118,3 +102,4 @@ describe Puppet::Face do
File.read(File.join(dir, modulename, 'doc', file))
end
end

View File

@ -3,11 +3,11 @@ require 'puppetx/puppetlabs/strings/yard/handlers/defined_type_handler'
require 'strings_spec/parsing'
describe "DefinedTypeHanlder" do
describe Puppetx::PuppetLabs::Strings::YARD::Handlers::DefinedTypeHandler do
include StringsSpec::Parsing
def the_definedtype()
Registry.at("foo::bar")
YARD::Registry.at("foo::bar")
end
it "should parse single-line documentation strings before a given defined type" do
@ -56,6 +56,6 @@ describe "DefinedTypeHanlder" do
parse(puppet_code, :puppet)
expect(Registry.all).to be_empty
expect(YARD::Registry.all).to be_empty
end
end

View File

@ -2,11 +2,11 @@ require 'spec_helper'
require 'puppetx/puppetlabs/strings/yard/handlers/host_class_handler'
require 'strings_spec/parsing'
describe "HostClassDefintion" do
describe Puppetx::PuppetLabs::Strings::YARD::Handlers::HostClassHandler do
include StringsSpec::Parsing
def the_hostclass()
Registry.at("foo::bar")
YARD::Registry.at("foo::bar")
end
it "should parse single-line documentation strings before a given class" do

View File

@ -2,15 +2,15 @@ require 'spec_helper'
require 'puppetx/puppetlabs/strings/yard/handlers/puppet_3x_function_handler'
require 'strings_spec/parsing'
describe "Puppet3xFunctionHanlder" do
describe Puppetx::PuppetLabs::Strings::YARD::Handlers::Puppet3xFunctionHandler do
include StringsSpec::Parsing
def the_method()
Registry.at("Puppet3xFunctions#the_function")
YARD::Registry.at("Puppet3xFunctions#the_function")
end
def the_namespace()
Registry.at("Puppet3xFunctions")
YARD::Registry.at("Puppet3xFunctions")
end
it "should parse single-line documentation strings before a given function" do

View File

@ -2,15 +2,15 @@ require 'spec_helper'
require 'puppetx/puppetlabs/strings/yard/handlers/puppet_4x_function_handler'
require 'strings_spec/parsing'
describe "Pupet4xFunctionHandler" do
describe Puppetx::PuppetLabs::Strings::YARD::Handlers::Puppet4xFunctionHandler do
include StringsSpec::Parsing
def the_method()
Registry.at("Puppet4xFunctions#the_function")
YARD::Registry.at("Puppet4xFunctions#the_function")
end
def the_namespace()
Registry.at("Puppet4xFunctions")
YARD::Registry.at("Puppet4xFunctions")
end
it "should parse single-line documentation strings before a given function" do
@ -60,6 +60,6 @@ describe "Pupet4xFunctionHandler" do
This is not ruby code
RUBY
expect(Registry.all).to be_empty
expect(YARD::Registry.all).to be_empty
end
end