Merge pull request #33 from iankronquist/dispatch-params/PDOC-37

Dispatch params/pdoc 37
This commit is contained in:
Henrik Lindberg 2015-07-13 15:52:01 -07:00
commit 2e3821c2af
2 changed files with 181 additions and 11 deletions

View File

@ -23,32 +23,100 @@ class Puppet4xFunctionHandler < YARD::Handlers::Ruby::Base
handles method_call(:create_function)
# Given a command node which represents code like this:
# param 'Optional[Type]', :value_type
# Extract the type name and type signature and return them as a array.
def extract_type_from_command command
return [] if command.children.length < 2 or command.children[1].children.length < 2
type_specifier = command.children[1]
# the parameter signature is the first child of the specifier and an
# identifier. Convert it to a string.
param_signature = type_specifier.children[0].source
# The parameter name is the second child of the specifier and a symbol.
# convert it to a string.
param_name_ident = type_specifier.jump :ident
return [] if param_name_ident == type_specifier
param_name = param_name_ident.source
[param_name, param_signature]
end
process do
name = process_parameters
method_arguments = []
# To attach the method parameters to the new code object, traverse the
# ruby AST until a node is found which defines a list of parameters.
# ruby AST until a node is found which defines a array of parameters.
# Then, traverse the children of the parameters, storing each identifier
# in the list of method arguments.
# in the array of method arguments.
obj = MethodObject.new(function_namespace, name) do |o|
end
# overload_signatures is a array of arrays of tuples or empty:
# overload_signatures = [
# [ # First function dispatch arguments
# # argument name, argument type
# ['arg0', 'Variant[String,Array[String]]'
# ['arg1', 'Optional[Type]']
# ],
# [ # Second function dispatch arguments
# ['arg0', 'Variant[String,Array[String]]'
# ['arg1', 'Optional[Type]']
# ['arg2', 'Any']
# ]
# ]
overload_signatures = []
statement.traverse do |node|
# Find all of the dispatch methods
if node.type == :ident and node.source == 'dispatch'
command = node.parent
do_block = command.jump :do_block
# If the command doesn't have a do_block we can't extract type info
if do_block == command
next
end
# Iterate through each of the children of the do block and build
# tuples of parameter names and parameter type signatures
do_block.children.first.children.each do |child|
overload_signatures <<= [extract_type_from_command(child)]
end
end
end
# If the overload_signatures array is empty because we couldn't find any
# dispatch blocks, then there must be one method named the same as the
# name of the function being created.
if overload_signatures.length == 0
statement.traverse do |node|
if node.type == :params
node.traverse do |params|
if params.type == :ident
# FIXME: Replace nil with parameter types
method_arguments << [params[0], nil]
# Find the function definition with the same name as the puppet
# function being created.
if (node.type == :def and node.children.first.type == :ident and
node.children.first.source == obj.name.to_s)
signature = []
# Find its parameters. If they don't exist, fine
params = node.jump :params
break if params == node
params.traverse do |param|
if param.type == :ident
# The parameters of Puppet functions with no defined dispatch are
# as though they are Any type.
signature << [param[0], 'Any']
end
end
overload_signatures <<= signature
# Now that the parameters have been found, break out of the traversal
break
end
end
end
obj.parameters = method_arguments
# Preserve this type information. We'll need it later when we look
# at the docstring.
obj.type_info = overload_signatures
# The yard docstring parser expects a array of arrays, not a array of
# arrays of arrays.
obj.parameters = overload_signatures.flatten(1)
obj['puppet_4x_function'] = true
@ -102,7 +170,7 @@ class Puppet4xFunctionHandler < YARD::Handlers::Ruby::Base
# @return [(String, Hash{String => String})]
def process_parameters
# Passing `false` to parameters excludes the block param from the returned
# list.
# array.
name, _ = statement.parameters(false).compact
name = process_element(name)
@ -114,7 +182,7 @@ class Puppet4xFunctionHandler < YARD::Handlers::Ruby::Base
# instead of `<<-`.
HEREDOC_START = /^<?<-/
# Turns an entry in the method parameter list into a string.
# Turns an entry in the method parameter array into a string.
#
# @param ele [YARD::Parser::Ruby::AstNode]
# @return [String]

View File

@ -71,4 +71,106 @@ describe PuppetX::PuppetLabs::Strings::YARD::Handlers::Puppet4xFunctionHandler d
RUBY
}.to output("#{expected_output_not_a_param}\n#{expected_output_also_not_a_param}\n").to_stdout_from_any_process
end
it "should not issue a warning when the parameter names match the docstring" do
expect {
parse <<-RUBY
# @param num_a [Integer] the first number to be compared
# @param num_b [Integer] the second number to be compared
Puppet::Functions.create_function(:max) do
def max(num_a, num_b)
num_a >= num_b ? num_a : num_b
end
end
RUBY
}.to output("").to_stdout_from_any_process
end
it "should not issue a warning when there are parametarized types and parameter names are the same" do
expect {
parse <<-RUBY
# @param num_a Integer[1,2] the first number to be compared
# @param num_b Integer[1,2] the second number to be compared
Puppet::Functions.create_function(:max) do
def max(num_a, num_b)
num_a >= num_b ? num_a : num_b
end
end
RUBY
}.to output("").to_stdout_from_any_process
end
it "should issue a warning when there are parametarized types and parameter names differ" do
expected_output_not_num_a = "[warn]: @param tag has unknown parameter" +
" name: not_num_a \n in file `(stdin)' near line 3"
expect {
parse <<-RUBY
# @param not_num_a Integer[1,2] the first number to be compared
# @param num_b Integer[1,2] the second number to be compared
Puppet::Functions.create_function(:max) do
dispatch max_1 do
param 'Integer[1,2]', :num_a
param 'Integer[1,2]', :num_b
end
def max_1(num_a, num_b)
num_a >= num_b ? num_a : num_b
end
end
RUBY
}.to output("#{expected_output_not_num_a}\n").to_stdout_from_any_process
end
it "should issue a warning if the parameter names do not match the docstring in dispatch method" do
expected_output_not_a_param = "[warn]: @param tag has unknown parameter" +
" name: not_a_param \n in file `(stdin)' near line 3"
expected_output_also_not_a_param = "[warn]: @param tag has unknown " +
"parameter name: also_not_a_param \n in file `(stdin)' near line 3"
expect {
parse <<-RUBY
# @param not_a_param Integer the first number to be compared
# @param also_not_a_param Integer the second number to be compared
Puppet::Functions.create_function(:max) do
dispatch max_1 do
param 'Integer[1,2]', :num_a
param 'Integer', :num_b
end
dispatch max_2 {
param 'String', :num_c
param 'String[1,2]', :num_d
}
def max_1(num_a, num_b)
num_a >= num_b ? num_a : num_b
end
def max_2(num_a, num_b)
num_a >= num_b ? num_a : num_b
end
end
RUBY
}.to output("#{expected_output_not_a_param}\n#{expected_output_also_not_a_param}\n").to_stdout_from_any_process
end
it "should not issue a warning if the parameter names do match the " +
"docstring in dispatch method" do
expect {
parse <<-RUBY
# @param num_a Integer the first number to be compared
# @param num_b Integer the second number to be compared
Puppet::Functions.create_function(:max) do
dispatch max_1 do
param 'Integer', :num_a
param 'Integer', :num_b
end
def max_1(num_a, num_b)
num_a >= num_b ? num_a : num_b
end
end
RUBY
}.to output("").to_stdout_from_any_process
end
end