2009-03-07 8 views
22

Ich möchte in Ruby einige ziemlich strikte Überlegungen anstellen. Ich möchte eine Funktion erstellen, die die Namen der Argumente der verschiedenen aufrufenden Funktionen höher auf dem Call-Stack zurückgibt (nur eine höhere wäre genug, aber warum dort aufhören?). Ich könnte Kernel.caller verwenden, zu der Datei gehen und die Argumentliste parsen, aber das wäre hässlich und unzuverlässig.So erhalten Sie Argumentnamen mit Hilfe der Reflektion

Die Funktion, die ich in der folgenden Art und Weise funktionieren würde, würde:

module A 
    def method1(tuti, fruity) 
    foo 
    end 
    def method2(bim, bam, boom) 
    foo 
    end 
    def foo 
    print caller_args[1].join(",") #the "1" mean one step up the call stack 
    end 

end 

A.method1 
#prints "tuti,fruity" 
A.method2 
#prints "bim, bam, boom" 

ich mit nicht aber bei Syntaxbaum suchte dagegen ParseTree oder ein ähnliches Werkzeug für diese Aufgabe, ist es nicht klar, wie es zu benutzen für diesen Zweck. Das Erstellen einer C-Erweiterung wie this ist eine andere Möglichkeit, aber es wäre schön, wenn jemand es schon für mich getan hätte.

Ich kann sehen, dass ich wahrscheinlich eine Art C-Erweiterung benötigen werde. Ich vermute, das bedeutet meine Frage ist, welche Kombination von C-Erweiterung würde am einfachsten funktionieren. Ich glaube nicht, dass Caller + ParseTree alleine ausreichen würde.

Soweit ich das gerne tun würde, anstatt zu sagen "automatisches Debugging", sollte ich vielleicht sagen, dass ich diese Funktionalität nutzen möchte, um die Aufruf- und Rückgabebedingungen von Funktionen automatisch zu überprüfen:

def add x, y 
    check_positive 
    return x + y 
end 

Wo check_positive eine Ausnahme auslösen würde, wenn x und y nicht positiv waren. Natürlich würde es mehr geben als das, aber hoffentlich gibt das genug Motivation.

+0

Es sieht aus wie Sie Ausführungsreihenfolge und Argumente bestimmen wollen. Gibt es einen Grund, warum Sie die Protokollierung nicht verwenden können? Sie könnten wahrscheinlich sogar eine Metaprogrammierung durchführen, um alle Methoden in der Klasse zu öffnen und automatisch die Argumente zu protokollieren, um das zu tun, was Sie wollen. – runako

Antwort

15

Ich schlage vor, Sie werfen einen Blick auf Merbs Aktion-Args-Bibliothek.

require 'rubygems' 
require 'merb' 

include GetArgs 

def foo(bar, zed=42) 
end 

method(:foo).get_args # => [[[:bar], [:zed, 42]], [:zed]] 

Wenn Sie nicht auf Merb abhängen wollen, können Sie die besten Teile aus the source code in github wählen und wählen.

4

Ich habe eine Methode, die recht teuer ist und funktioniert nur fast.

$shadow_stack = [] 

set_trace_func(lambda { 
    |event, file, line, id, binding, classname| 
    if event == "call" 
    $shadow_stack.push(eval("local_variables", binding)) 
    elsif event == "return" 
    $shadow_stack.pop 
    end 
}) 

def method1(tuti, fruity) 
    foo 
end 
def method2(bim, bam, boom) 
    foo 
    x = 10 
    y = 3 
end 

def foo 
    puts $shadow_stack[-2].join(", ") 
end 

method1(1,2) 
method2(3,4,4) 

Ausgänge

tuti, fruity 
bim, bam, boom, x, y 

Ich bin neugierig, warum Sie eine solche Funktionalität in einer solchen Art und Weise verallgemeinert wollen würde.

Ich bin neugierig, wie Sie denken, dass diese Funktionalität für das automatische Debuggen ermöglichen würde? Sie müssen immer noch Anrufe an Ihre "foo" -Funktion injizieren. Tatsächlich ist etwas, das auf set_trace_func basiert, eher in der Lage, automatisch zu sein, da Sie bestehenden Code nicht berühren müssen. In der Tat wird debug.rb in Bezug auf set_trace_func implementiert.

Die Lösungen für Ihre genaue Frage sind in der Tat im Grunde, wie Sie beschrieben. Verwenden Sie Caller + Partree, oder öffnen Sie die Datei und greifen Sie die Daten auf diese Weise. Es gibt keine Reflexionsfähigkeit, von der ich weiß, dass Sie die Namen von Argumenten bekommen können. Sie können meine Lösung genehmigen, indem Sie das zugehörige Methodenobjekt ergreifen und #arity aufrufen, um dann zu folgern, was von local_variables Argumente sind, aber obwohl es scheint, dass das Ergebnis dieser Funktion geordnet ist, bin ich nicht sicher, dass man sich darauf verlassen kann. Wenn es Ihnen nichts ausmacht, wenn Sie fragen, was Sie mit den Daten und der Schnittstelle, die Sie beschreiben, tun werden? Automatisches Debugging war nicht das, was mir anfangs in den Sinn kam, als ich mir Anwendungen für diese Funktionalität vorstellte, obwohl es vielleicht meine Vorstellungskraft ist.


Aha!

Ich würde dies anders dann nähern. Es gibt mehrere Ruby-Bibliotheken, die bereits vertragsgemäß arbeiten, einschließlich Ruby-Contract, RDBC usw.

Eine weitere Option ist so etwas wie zu schreiben:

def positive 
    lambda { |x| x >= 0 } 
end 

def any 
    lambda { |x| true } 
end 

class Module 
    def define_checked_method(name, *checkers, &body) 
    define_method(name) do |*args| 
     unless checkers.zip(args).all? { |check, arg| check[arg] } 
     raise "bad argument" 
     end 
     body.call(*args) 
    end 
    end 
end 

class A 
    define_checked_method(:add, positive, any) do |x, y| 
    x + y 
    end 
end 

a = A.new 
p a.add(3, 2) 
p a.add(3, -1) 
p a.add(-4, 2) 

Ausgänge

5 
2 
checked_rb.rb:13:in `add': bad argument (RuntimeError) 
     from checked_rb.rb:29 

Natürlich ist das viel anspruchsvoller gemacht werden kann, und in der Tat, das ist Teil dessen, was die Bibliotheken, die ich zur Verfügung gestellt erwähnt, aber vielleicht ist dies ein Weg, um Sie dahin zu bringen, wohin Sie gehen wollen, ohne notwendigerweise den Weg einzuschlagen, den Sie für den Weg dorthin benutzen wollten?

+0

In der Tat unterscheidet die Methode, die Sie beschreiben eindeutig nicht Argumente von lokalen Variablen zu unterscheiden, während auch nicht automatisch arbeiten –

+0

Nun, das funktioniert für die Gestaltung von Vertrag, aber es scheint mehr als ein wenig klunkig im Vergleich zu nur mit einer einzigen Funktion, die Sie anrufen könnten . Das Ideal ist * einfach * hinzufügen und entfernen Sie die Schecks. –

-3

In der Tat, die Methode, die Sie eindeutig beschreiben nicht Argumente von lokalen Variablen zu unterscheiden, während auch automatisch

arbeiten andernfalls Das ist, weil, was Sie versuchen zu tun ist, nicht etwas, das unterstützt wird. Es ist möglich (in Ruby ist alles möglich), aber es gibt keinen dokumentierten oder bekannten Weg, dies zu tun.

Entweder können Sie das Backtrace eva lasen, was Logan vorgeschlagen hat, oder Sie können Ihren C-Compiler ausstechen und Quellcode für Ruby hacken. Ich bin ziemlich zuversichtlich, dass es keine anderen Möglichkeiten gibt, dies zu tun.

39

In Ruby 1.9.2, können Sie trivialer die Parameterliste aller Proc bekommen (und damit natürlich auch jeden Method oder UnboundMethod) mit Proc#parameters:

A.instance_method(:method1).parameters # => [[:req, :tuti], [:req, :fruity]] 

Das Format ist ein Array von Paaren Symbole: Typ (erforderlich, optional, Pause, Block) und Name. obwohl

Für das Format, das Sie versuchen wollen

A.instance_method(:method1).parameters.map(&:last).map(&:to_s) 
    # => ['tuti', 'fruity'] 

Natürlich, das noch nicht, dass Sie mit dem Anrufer Zugriff nicht geben.

+0

Dies gibt auch keine Standardwerte zurück. Zum Beispiel: 'def person (name:" calvin ")'. 'method.parameters' gibt' [[:::, name]]]. –

+2

Standardwerte werden dynamisch ausgewertet, Sie können keine statischen Informationen über sie erhalten. Was sollte 'def foo (par = rand()); Methode (: foo) .Parameters Rückkehr? –

+0

Was bedeutet "instance_method" hier? – fraxture

1

wenn Sie den Wert für die Standardwerte wollen auch, gibt es die „Argumente“ gem

$ gem install rdp-arguments 
$ irb 
>> require 'arguments' 
>> require 'test.rb' # class A is defined here 
>> Arguments.names(A, :go) 
Verwandte Themen