1

I want to use metaprogramming to introduce docstring feature into Ruby language.

Here is very early prototype of the code I've written so far:

module Docstrings
  def doc(docstring)
    @docstrings ||= {}
    if docstring.is_a? String
      # Ruby 2.0 trick to get a caller of the method
      method_caller = caller_locations(1,1)[0].label.to_sym

      @docstrings[method_caller] ||= docstring
    else
      @docstrings[docstring]
    end
  end
end

# lets include it temporarily to see how it works
include Docstrings

class Method
   include Docstrings
end

doc "Hello"
puts doc :"<main>" # => "Hello"

It works. But, sadly:

def square(x)
  doc """This method returns square of x"""

  x * x
end

doc(:square) # => nil

This is not working as I expected.

square(2)
doc(:square) # => """This method returns square of x"""

It will add docstring only when method square is invoked at least 1 time which is obvious.

My question is is it possible to implement in a way that a docstring will be attached more to a method, not to invocation of that method? I'm looking for hints not for solution, please tell me where should I look :)

4

2 回答 2

2

这似乎有效:(虽然,它不是真正的元编程,只是一个黑客)

假设你想要这个:

  def add_these(a, b)
    doc "This function adds two numbers"
    a + b
  end

whatsthedocdoc?(:add_these) # => "This function adds two numbers"

class Object
  def whatsthedocdoc?(method)
    meth = method(method.to_sym)
    sours = meth.source
    puts sours.lines.grep(/doc/).join.gsub(/doc|\"/, "").strip
  end
end

但这并不是那么简单。上面的代码片段假设方法是在main对象空间中定义的。让我们考虑这个例子:

class A
  def add_these(a, b)
    doc "This method adds two numbers."
  end
end

在此示例中,whatsthedocdoc?方法内的代码应更改为:

def whatsthedocdoc?(string)
  receiver, meth = string.split(/\#/)
  meth_instance = receiver.method(meth.to_sym)
  sours = meth_instance.source
  # rest is the same as above
end

并且可以像这样查看文档:

whatsthedocdoc?("A#add_these") # => This method adds two numbers.

现在不是很整洁吧?


哦!还有另一种极端情况:类方法

class A
  def self.add_these(a, b)
    doc "This too, adds two numbers"
  end
end

你明白了……

于 2013-08-11T16:47:57.960 回答
0

使用 ruby​​ 钩子,我们可以拦截方法定义。所以这个解决方案有效,但 doc() 必须在方法 def 之前定义:

class Object

    def self.method_added(s)
        @docString||={}
        if defined?(@lastdoc)&& @lastdoc!=""
            @docString[s]=@lastdoc
            @lastdoc=""
        else
            @docString[s]="!uncommented !"
        end
    end
    def self.defdoc(s)
        @lastdoc=s
    end
    ################ report
    def self.get_docs()
        @docString||={}
        puts "Doc for #{self.to_s} is : \n"
        @docString.each  {|k,v|puts " #{k} : #{v}\n"}
        puts "end for #{self.to_s}\n"
    end
    def get_docs(*klass)
        klass.each {|c| c.get_docs()}
    end
end
################# exemple :
class Foo
    defdoc "toto() defined"
    def toto ;end
    def titi ;end
    defdoc "tutu() defined"
    def tutu ;end
    def tata ;end
end
class Fee < Foo
end

get_docs Foo,Fee

这给:

Doc for Foo is :
 toto : toto() defined
 titi : !uncommented !
 tutu : tutu() defined
 tata : !uncommented !
end for Foo
Doc for Fee is :
end for Fee
于 2013-08-11T16:42:59.920 回答