Rocky Bernstein | 5 May 13:05

docstrings (Was Re: Uniform RDoc markup)

Speaking of Ruby and documentation, I'd like to put in a suggestion for  document strings. Emacs Lisp and Python both have it.  And in both is a really good thing and made heavy use of it. It means at run time one can get very specific documentation information.

I leave open where and how this information is tagged. In both Emacs Lisp and Python this is a string that appears after the method "signature". However
it is also possible that something could be done to use the comment before the method definition, the same as rdoc currently uses.

Finally, I'd like to put in a plug for Paul Brannan's work in ruby-internal for getting method signatures dynamically.

Paul Brannan to me
show details Apr 29 (6 days ago)
Reply
On Tue, Apr 29, 2008 at 11:44:37AM -0400, Rocky Bernstein wrote:
> Some things though relating to method signatures. First, in contrast to what
> the nodewrap documentation <http://rubystuff.org/nodewrap/> seems to
> suggest, adding:
>     require 'methodsig'
> is what worked for me. Is this correct, or am I doing something wrong?

Yes, that's correct, and I've fixed the webpage.

> Also, methods implemented in C give an error when trying to get the
> signature:
>   NoMethodError: undefined method `argument_names' for #<Node::CFUNC
> cfnc=-1209864880, argc=-1>

Good catch.  I'm not surprised by this at all.

I've entered a bug:

http://rubyforge.org/tracker/index.php?func=detail&aid=19854&group_id=313&atid=1268

> Attached is a patch that add an "impl" instance variable to the signature
> which right now can be :cfunc or nil if not a C function. I'm not sure what
> other possibilities there are but no doubt there may be other interesting
> kinds of method implementations. For example, perhaps a "compiled" method of
> YARV might be an interesting thing to note here.

Thank you for the patch.  It needs unit tests and a few fixes before I
can commit.  Do you want to do this or should I add it to my todo list?
I've put my comments below.

BTW, all my nodewrap development is now going toward ruby-internal on
github, so this patch should really be against
lib/internal/method/signature.rb rather than lib/methodsig.rb.

> Another problem I've noticed is that classtree gives an error:
> Object.classtree
> ArgumentError: cyclic include detected
>     from ./classtree.rb:20:in `real_superclass'
>     from ./classtree.rb:20:in `classtree'
>     from ./classtree.rb:22:in `classtree'
>     from ./classtree.rb:25:in `classtree'
>     from (irb):2

Hmm, I wonder when this broke.  This is why unit tests are important. :)

http://rubyforge.org/tracker/index.php?func=detail&aid=19853&group_id=313&atid=1268

> Unless this is a known problem that you care to elucidate on, I'll probably
> look at this some other day.
>
> As before, thanks for the great work.

Thanks for your feedback!

> cvs diff -u
> cvs diff: Diffing .
> Index: methodsig.rb
> ===================================================================
> RCS file: /var/cvs/nodewrap/nodewrap/lib/methodsig.rb,v
> retrieving revision 1.19
> diff -u -r1.19 methodsig.rb
> --- methodsig.rb      19 Feb 2008 03:40:41 -0000      1.19
> +++ methodsig.rb      29 Apr 2008 15:13:57 -0000
> <at> <at> -53,7 +53,11 <at> <at>
>    # Return the names of the arguments this method takes, in the order in
>    # which they appear in the argument list.
>    def argument_names
> -    return self.body.argument_names
> +    if <at> impl == :cfunc
> +      return ['argc']

This isn't right.  The cfunc still has argument names; we just don't
know what they are.  For a method that takes 3 args argument_names()
should return an array with 3 elements, even if those elements are just
'arg1', 'arg2,' and 'arg3'.

Also, using 'if' like this isn't OO-style.  It's better to make use of
polymorphism instead.  I think this can be done implementing
Node::CFUNC#local_vars, Node::CFUNC#argument_names, and the like.

> +    else
> +      return self.body.argument_names
> +   end
>    end
>
>    def args_node
> <at> <at> -83,28 +87,32 <at> <at>
>    # Return a hash mapping each argument name to a description of that
>    # argument.
>    def arguments
> -    names = self.argument_names()
> -    block_arg = self.block_arg()
> -
>      args = {}
> -    names.each do |name|
> -      args[name] = Argument.new(name, nil, nil, false, false)
> -    end
> +    if <at> impl == :cfunc
> +      args['argc'] = self.body.argc

All members of the args hash should be of type Argument.

> +    else
> +      names = self.argument_names()
> +      block_arg = self.block_arg()
> +
> +      names.each do |name|
> +        args[name] = Argument.new(name, nil, nil, false, false)
> +      end
>
> -    # Optional args
> -    args_node = args_node()
> -    set_optional_args(args, args_node, names)
> +      # Optional args
> +      args_node = args_node()
> +      set_optional_args(args, args_node, names)
>

Cfunc never has optional args (as your code correctly handles),

> -    # Rest arg
> -    if self.rest_arg then
> -      rest_name = names[rest_arg]
> -      args[rest_name] = Argument.new(rest_name, nil, nil, true, false)
> -    end
> +      # Rest arg
> +      if self.rest_arg then
> +        rest_name = names[rest_arg]
> +        args[rest_name] = Argument.new(rest_name, nil, nil, true, false)
> +      end

But it can have a single rest arg in some cases:

irb(main):006:0> method(:puts).body
=> #<Node::CFUNC cfnc=47601960251488, argc=-1>

> -    # Block arg
> -    if block_arg then
> -      block_name = names[block_arg]
> -      args[block_name] = Argument.new(block_name, nil, nil, false, true)
> +      # Block arg
> +      if block_arg then
> +        block_name = names[block_arg]
> +        args[block_name] = Argument.new(block_name, nil, nil, false, true)
> +      end
>      end

CFUNC should never have an explicit block arg (again, your code is
correct).

>
>      return args
> <at> <at> -112,17 +120,19 <at> <at>
>
>    # An abstraction for a method signature.
>    class Signature
> -    attr_reader :origin_class, :name, :arg_names, :args
> +    attr_reader :origin_class, :name, :arg_names, :args, :impl
>
> -    def initialize(origin_class, name, arg_names, args)
> +    def initialize(origin_class, name, arg_names, args, impl=nil)
>         <at> origin_class = origin_class
>         <at> name = name
>         <at> arg_names = arg_names
>         <at> args = args
> +       <at> impl = impl
>      end
>
>      def to_s
> -      return "#{ <at> origin_class}\##{ <at> name}(#{param_list})"
> +      impl = :cfunc == <at> impl ? ' C function' : ''
> +      return "#{ <at> origin_class}\##{ <at> name}(#{param_list})#{impl}"
>      end
>
>      def param_list
> <at> <at> -155,13 +165,21 <at> <at>
>  class Method
>    include MethodSig
>
> +  def initialize
> +     <at> impl = nil
> +  end
> +
>    # Return a String representing the method's signature.
>    def signature
> +    if self.body.respond_to?(:nd_type) and 'CFUNC' == self.body.nd_type.name
> +       <at> impl = :cfunc
> +    end
>      return Signature.new(
>          attached_class(),
>          method_oid().to_s,
>          argument_names(),
> -        arguments())
> +        arguments(),
> +         <at> impl)
>    end
>  end
>
> <at> <at> -170,11 +188,15 <at> <at>
>
>    # Return a String representing the method's signature.
>    def signature
> +    if self.body.respond_to?(:nd_type) and 'CFUNC' == self.body.nd_type.name
> +       <at> impl = :cfunc
> +    end
>      return Signature.new(
>          origin_class(),
>          method_oid().to_s,
>          argument_names(),
> -        arguments())
> +        arguments(),
> +         <at> impl)
>    end
>  end
>
> <at> <at> -386,5 +408,6 <at> <at>
>
>    def foo(foo, bar=[1,2], *args, &block); end
>    puts method(:foo).signature
> +  puts method(:caller).signature
>  end

Reply
Forward

On Mon, May 5, 2008 at 2:55 AM, Ryan Davis <ryand-ruby <at> zenspider.com> wrote:

On May 4, 2008, at 13:37 , Phil Hagelberg wrote:

"Jeremy McAnally" <jeremymcanally <at> gmail.com> writes:

Would there be any resistance to making the markup of the RDoc
throughout the core code more consistent?

I would be very much in favour of seeing stronger conventions applied
throughout the Ruby community in the standard library, in gems, and
elsewhere. That's one of the things I miss from writing Emacs Lisp; it
has very specific guidelines[1]

I guess Phil brings up a good point... emacs lisp doco is a breeze to read.

I don't mind any work making rdoc more consistent, but I'll resist any efforts to make it so damn structured. Do we really need to have an h4 tag for the parameters, options, returns, examples sections??? Let's leave javadoc to the java ppl and do something cleaner and clearer.




Gmane