class UnboundMethod
Ruby unterstützt zwei Formen von objektifizierten Methoden. Die Klasse Method wird verwendet, um Methoden darzustellen, die mit einem bestimmten Objekt verknüpft sind: Diese Methodenobjekte sind an dieses Objekt gebunden. Gebundene Methodenobjekte für ein Objekt können mit Object#method erstellt werden.
Ruby unterstützt auch ungebundene Methoden; Methodenobjekte, die nicht mit einem bestimmten Objekt verknüpft sind. Diese können entweder durch Aufruf von Module#instance_method oder durch Aufruf von unbind auf einem gebundenen Methodenobjekt erstellt werden. Das Ergebnis beider Aktionen ist ein UnboundMethod-Objekt.
Ungebundene Methoden können nur aufgerufen werden, nachdem sie an ein Objekt gebunden wurden. Dieses Objekt muss eine Art von kind_of? der ursprünglichen Klasse der Methode sein.
class Square def area @side * @side end def initialize(side) @side = side end end area_un = Square.instance_method(:area) s = Square.new(12) area = area_un.bind(s) area.call #=> 144
Ungebundene Methoden sind ein Verweis auf die Methode zum Zeitpunkt ihrer Objektifizierung: Nachfolgende Änderungen an der zugrunde liegenden Klasse wirken sich nicht auf die ungebundene Methode aus.
class Test def test :original end end um = Test.instance_method(:test) class Test def test :modified end end t = Test.new t.test #=> :modified um.bind(t).call #=> :original
Öffentliche Instanzmethoden
Source
#define unbound_method_eq method_eq
Zwei ungebundene Methodenobjekte sind gleich, wenn sie sich auf dieselbe Methodendefinition beziehen.
Array.instance_method(:each_slice) == Enumerable.instance_method(:each_slice) #=> true Array.instance_method(:sum) == Enumerable.instance_method(:sum) #=> false, Array redefines the method for efficiency
Source
static VALUE
method_arity_m(VALUE method)
{
int n = method_arity(method);
return INT2FIX(n);
}
Gibt einen Hinweis auf die Anzahl der Argumente zurück, die eine Methode akzeptiert. Gibt eine nicht-negative Ganzzahl für Methoden zurück, die eine feste Anzahl von Argumenten annehmen. Für Ruby-Methoden, die eine variable Anzahl von Argumenten annehmen, gibt -n-1 zurück, wobei n die Anzahl der erforderlichen Argumente ist. Schlüsselwortargumente werden als ein einzelnes zusätzliches Argument betrachtet, wobei dieses Argument obligatorisch ist, wenn ein Schlüsselwortargument obligatorisch ist. Für in C geschriebene Methoden wird -1 zurückgegeben, wenn der Aufruf eine variable Anzahl von Argumenten annimmt.
class C def one; end def two(a); end def three(*a); end def four(a, b); end def five(a, b, *c); end def six(a, b, *c, &d); end def seven(a, b, x:0); end def eight(x:, y:); end def nine(x:, y:, **z); end def ten(*a, x:, y:); end end c = C.new c.method(:one).arity #=> 0 c.method(:two).arity #=> 1 c.method(:three).arity #=> -1 c.method(:four).arity #=> 2 c.method(:five).arity #=> -3 c.method(:six).arity #=> -3 c.method(:seven).arity #=> -3 c.method(:eight).arity #=> 1 c.method(:nine).arity #=> 1 c.method(:ten).arity #=> -2 "cat".method(:size).arity #=> 0 "cat".method(:replace).arity #=> 1 "cat".method(:squeeze).arity #=> -1 "cat".method(:count).arity #=> -1
Source
static VALUE
umethod_bind(VALUE method, VALUE recv)
{
VALUE methclass, klass, iclass;
const rb_method_entry_t *me;
const struct METHOD *data;
TypedData_Get_Struct(method, struct METHOD, &method_data_type, data);
convert_umethod_to_method_components(data, recv, &methclass, &klass, &iclass, &me, true);
struct METHOD *bound;
method = TypedData_Make_Struct(rb_cMethod, struct METHOD, &method_data_type, bound);
RB_OBJ_WRITE(method, &bound->recv, recv);
RB_OBJ_WRITE(method, &bound->klass, klass);
RB_OBJ_WRITE(method, &bound->iclass, iclass);
RB_OBJ_WRITE(method, &bound->owner, methclass);
RB_OBJ_WRITE(method, &bound->me, me);
return method;
}
Bindet umeth an obj. Wenn Klass die Klasse war, von der umeth erhalten wurde, muss obj.kind_of?(Klass) wahr sein.
class A def test puts "In test, class = #{self.class}" end end class B < A end class C < B end um = B.instance_method(:test) bm = um.bind(C.new) bm.call bm = um.bind(B.new) bm.call bm = um.bind(A.new) bm.call
ergibt
In test, class = C In test, class = B prog.rb:16:in `bind': bind argument must be an instance of B (TypeError) from prog.rb:16
Source
static VALUE
umethod_bind_call(int argc, VALUE *argv, VALUE method)
{
rb_check_arity(argc, 1, UNLIMITED_ARGUMENTS);
VALUE recv = argv[0];
argc--;
argv++;
VALUE passed_procval = rb_block_given_p() ? rb_block_proc() : Qnil;
rb_execution_context_t *ec = GET_EC();
const struct METHOD *data;
TypedData_Get_Struct(method, struct METHOD, &method_data_type, data);
const rb_callable_method_entry_t *cme = rb_callable_method_entry(CLASS_OF(recv), data->me->called_id);
if (data->me == (const rb_method_entry_t *)cme) {
vm_passed_block_handler_set(ec, proc_to_block_handler(passed_procval));
return rb_vm_call_kw(ec, recv, cme->called_id, argc, argv, cme, RB_PASS_CALLED_KEYWORDS);
}
else {
VALUE methclass, klass, iclass;
const rb_method_entry_t *me;
convert_umethod_to_method_components(data, recv, &methclass, &klass, &iclass, &me, false);
struct METHOD bound = { recv, klass, 0, methclass, me };
return call_method_data(ec, &bound, argc, argv, passed_procval, RB_PASS_CALLED_KEYWORDS);
}
}
Bindet umeth an recv und ruft dann die Methode mit den angegebenen Argumenten auf. Dies ist semantisch äquivalent zu umeth.bind(recv).call(args, ...).
Source
static VALUE
method_clone(VALUE self)
{
VALUE clone;
struct METHOD *orig, *data;
TypedData_Get_Struct(self, struct METHOD, &method_data_type, orig);
clone = TypedData_Make_Struct(CLASS_OF(self), struct METHOD, &method_data_type, data);
rb_obj_clone_setup(self, clone, Qnil);
RB_OBJ_WRITE(clone, &data->recv, orig->recv);
RB_OBJ_WRITE(clone, &data->klass, orig->klass);
RB_OBJ_WRITE(clone, &data->iclass, orig->iclass);
RB_OBJ_WRITE(clone, &data->owner, orig->owner);
RB_OBJ_WRITE(clone, &data->me, rb_method_entry_clone(orig->me));
return clone;
}
Gibt eine Kopie dieser Methode zurück.
class A def foo return "bar" end end m = A.new.method(:foo) m.call # => "bar" n = m.clone.call # => "bar"
Zwei ungebundene Methodenobjekte sind gleich, wenn sie sich auf dieselbe Methodendefinition beziehen.
Array.instance_method(:each_slice) == Enumerable.instance_method(:each_slice) #=> true Array.instance_method(:sum) == Enumerable.instance_method(:sum) #=> false, Array redefines the method for efficiency
Source
static VALUE
method_hash(VALUE method)
{
struct METHOD *m;
st_index_t hash;
TypedData_Get_Struct(method, struct METHOD, &method_data_type, m);
hash = rb_hash_start((st_index_t)m->recv);
hash = rb_hash_method_entry(hash, m->me);
hash = rb_hash_end(hash);
return ST2FIX(hash);
}
Gibt einen Hash-Wert zurück, der dem Methodenobjekt entspricht.
Siehe auch Object#hash.
Source
static VALUE
method_inspect(VALUE method)
{
struct METHOD *data;
VALUE str;
const char *sharp = "#";
VALUE mklass;
VALUE defined_class;
TypedData_Get_Struct(method, struct METHOD, &method_data_type, data);
str = rb_sprintf("#<% "PRIsVALUE": ", rb_obj_class(method));
mklass = data->iclass;
if (!mklass) mklass = data->klass;
if (RB_TYPE_P(mklass, T_ICLASS)) {
/* TODO: I'm not sure why mklass is T_ICLASS.
* UnboundMethod#bind() can set it as T_ICLASS at convert_umethod_to_method_components()
* but not sure it is needed.
*/
mklass = RBASIC_CLASS(mklass);
}
if (data->me->def->type == VM_METHOD_TYPE_ALIAS) {
defined_class = data->me->def->body.alias.original_me->owner;
}
else {
defined_class = method_entry_defined_class(data->me);
}
if (RB_TYPE_P(defined_class, T_ICLASS)) {
defined_class = RBASIC_CLASS(defined_class);
}
if (UNDEF_P(data->recv)) {
// UnboundMethod
rb_str_buf_append(str, rb_inspect(defined_class));
}
else if (RCLASS_SINGLETON_P(mklass)) {
VALUE v = RCLASS_ATTACHED_OBJECT(mklass);
if (UNDEF_P(data->recv)) {
rb_str_buf_append(str, rb_inspect(mklass));
}
else if (data->recv == v) {
rb_str_buf_append(str, rb_inspect(v));
sharp = ".";
}
else {
rb_str_buf_append(str, rb_inspect(data->recv));
rb_str_buf_cat2(str, "(");
rb_str_buf_append(str, rb_inspect(v));
rb_str_buf_cat2(str, ")");
sharp = ".";
}
}
else {
mklass = data->klass;
if (RCLASS_SINGLETON_P(mklass)) {
VALUE v = RCLASS_ATTACHED_OBJECT(mklass);
if (!(RB_TYPE_P(v, T_CLASS) || RB_TYPE_P(v, T_MODULE))) {
do {
mklass = RCLASS_SUPER(mklass);
} while (RB_TYPE_P(mklass, T_ICLASS));
}
}
rb_str_buf_append(str, rb_inspect(mklass));
if (defined_class != mklass) {
rb_str_catf(str, "(% "PRIsVALUE")", defined_class);
}
}
rb_str_buf_cat2(str, sharp);
rb_str_append(str, rb_id2str(data->me->called_id));
if (data->me->called_id != data->me->def->original_id) {
rb_str_catf(str, "(%"PRIsVALUE")",
rb_id2str(data->me->def->original_id));
}
if (data->me->def->type == VM_METHOD_TYPE_NOTIMPLEMENTED) {
rb_str_buf_cat2(str, " (not-implemented)");
}
// parameter information
{
VALUE params = rb_method_parameters(method);
VALUE pair, name, kind;
const VALUE req = ID2SYM(rb_intern("req"));
const VALUE opt = ID2SYM(rb_intern("opt"));
const VALUE keyreq = ID2SYM(rb_intern("keyreq"));
const VALUE key = ID2SYM(rb_intern("key"));
const VALUE rest = ID2SYM(rb_intern("rest"));
const VALUE keyrest = ID2SYM(rb_intern("keyrest"));
const VALUE block = ID2SYM(rb_intern("block"));
const VALUE nokey = ID2SYM(rb_intern("nokey"));
int forwarding = 0;
rb_str_buf_cat2(str, "(");
if (RARRAY_LEN(params) == 3 &&
RARRAY_AREF(RARRAY_AREF(params, 0), 0) == rest &&
RARRAY_AREF(RARRAY_AREF(params, 0), 1) == ID2SYM('*') &&
RARRAY_AREF(RARRAY_AREF(params, 1), 0) == keyrest &&
RARRAY_AREF(RARRAY_AREF(params, 1), 1) == ID2SYM(idPow) &&
RARRAY_AREF(RARRAY_AREF(params, 2), 0) == block &&
RARRAY_AREF(RARRAY_AREF(params, 2), 1) == ID2SYM('&')) {
forwarding = 1;
}
for (int i = 0; i < RARRAY_LEN(params); i++) {
pair = RARRAY_AREF(params, i);
kind = RARRAY_AREF(pair, 0);
if (RARRAY_LEN(pair) > 1) {
name = RARRAY_AREF(pair, 1);
}
else {
// FIXME: can it be reduced to switch/case?
if (kind == req || kind == opt) {
name = rb_str_new2("_");
}
else if (kind == rest || kind == keyrest) {
name = rb_str_new2("");
}
else if (kind == block) {
name = rb_str_new2("block");
}
else if (kind == nokey) {
name = rb_str_new2("nil");
}
else {
name = Qnil;
}
}
if (kind == req) {
rb_str_catf(str, "%"PRIsVALUE, name);
}
else if (kind == opt) {
rb_str_catf(str, "%"PRIsVALUE"=...", name);
}
else if (kind == keyreq) {
rb_str_catf(str, "%"PRIsVALUE":", name);
}
else if (kind == key) {
rb_str_catf(str, "%"PRIsVALUE": ...", name);
}
else if (kind == rest) {
if (name == ID2SYM('*')) {
rb_str_cat_cstr(str, forwarding ? "..." : "*");
}
else {
rb_str_catf(str, "*%"PRIsVALUE, name);
}
}
else if (kind == keyrest) {
if (name != ID2SYM(idPow)) {
rb_str_catf(str, "**%"PRIsVALUE, name);
}
else if (i > 0) {
rb_str_set_len(str, RSTRING_LEN(str) - 2);
}
else {
rb_str_cat_cstr(str, "**");
}
}
else if (kind == block) {
if (name == ID2SYM('&')) {
if (forwarding) {
rb_str_set_len(str, RSTRING_LEN(str) - 2);
}
else {
rb_str_cat_cstr(str, "...");
}
}
else {
rb_str_catf(str, "&%"PRIsVALUE, name);
}
}
else if (kind == nokey) {
rb_str_buf_cat2(str, "**nil");
}
if (i < RARRAY_LEN(params) - 1) {
rb_str_buf_cat2(str, ", ");
}
}
rb_str_buf_cat2(str, ")");
}
{ // source location
VALUE loc = rb_method_location(method);
if (!NIL_P(loc)) {
rb_str_catf(str, " %"PRIsVALUE":%"PRIsVALUE,
RARRAY_AREF(loc, 0), RARRAY_AREF(loc, 1));
}
}
rb_str_buf_cat2(str, ">");
return str;
}
Gibt eine menschenlesbare Beschreibung der zugrunde liegenden Methode zurück.
"cat".method(:count).inspect #=> "#<Method: String#count(*)>" (1..3).method(:map).inspect #=> "#<Method: Range(Enumerable)#map()>"
In letzterem Fall enthält die Methodenspezifikation den "Besitzer" der ursprünglichen Methode (Enumerable-Modul, das in Range aufgenommen wird).
inspect bietet auch, wenn möglich, Methodensignatur (Aufrufsequenz) und Quellcode-Speicherort.
require 'net/http' Net::HTTP.method(:get).inspect #=> "#<Method: Net::HTTP.get(uri_or_host, path=..., port=...) <skip>/lib/ruby/2.7.0/net/http.rb:457>"
... in der Argumentdefinition bedeutet, dass das Argument optional ist (einen Standardwert hat).
Für in C definierte Methoden (Sprachkern und Erweiterungen) können Speicherort und Argumentnamen nicht extrahiert werden, und es werden nur generische Informationen in Form von * (beliebige Anzahl von Argumenten) oder _ (ein Positionsargument) bereitgestellt.
"cat".method(:count).inspect #=> "#<Method: String#count(*)>" "cat".method(:+).inspect #=> "#<Method: String#+(_)>""
Source
static VALUE
method_name(VALUE obj)
{
struct METHOD *data;
TypedData_Get_Struct(obj, struct METHOD, &method_data_type, data);
return ID2SYM(data->me->called_id);
}
Gibt den Namen der Methode zurück.
Source
static VALUE
method_original_name(VALUE obj)
{
struct METHOD *data;
TypedData_Get_Struct(obj, struct METHOD, &method_data_type, data);
return ID2SYM(data->me->def->original_id);
}
Gibt den ursprünglichen Namen der Methode zurück.
class C def foo; end alias bar foo end C.instance_method(:bar).original_name # => :foo
Source
static VALUE
method_owner(VALUE obj)
{
struct METHOD *data;
TypedData_Get_Struct(obj, struct METHOD, &method_data_type, data);
return data->owner;
}
Gibt die Klasse oder das Modul zurück, auf dem diese Methode definiert ist. Mit anderen Worten,
meth.owner.instance_methods(false).include?(meth.name) # => true
gilt, solange die Methode nicht entfernt/undefiniert/ersetzt wird (mit private_instance_methods anstelle von instance_methods, wenn die Methode privat ist).
Siehe auch Method#receiver.
(1..3).method(:map).owner #=> Enumerable
Source
static VALUE
rb_method_parameters(VALUE method)
{
return method_def_parameters(rb_method_def(method));
}
Gibt die Parameterinformationen dieser Methode zurück.
def foo(bar); end method(:foo).parameters #=> [[:req, :bar]] def foo(bar, baz, bat, &blk); end method(:foo).parameters #=> [[:req, :bar], [:req, :baz], [:req, :bat], [:block, :blk]] def foo(bar, *args); end method(:foo).parameters #=> [[:req, :bar], [:rest, :args]] def foo(bar, baz, *args, &blk); end method(:foo).parameters #=> [[:req, :bar], [:req, :baz], [:rest, :args], [:block, :blk]]
Source
VALUE
rb_method_location(VALUE method)
{
return method_def_location(rb_method_def(method));
}
Gibt den Speicherort zurück, an dem die Methode definiert wurde. Das zurückgegebene Array enthält
(1) the Ruby source filename (2) the line number where the definition starts (3) the column number where the definition starts (4) the line number where the definition ends (5) the column number where the definitions ends
Diese Methode gibt nil zurück, wenn die Methode nicht in Ruby definiert wurde (d.h. nativer Code).
Source
static VALUE
method_super_method(VALUE method)
{
const struct METHOD *data;
VALUE super_class, iclass;
ID mid;
const rb_method_entry_t *me;
TypedData_Get_Struct(method, struct METHOD, &method_data_type, data);
iclass = data->iclass;
if (!iclass) return Qnil;
if (data->me->def->type == VM_METHOD_TYPE_ALIAS && data->me->defined_class) {
super_class = RCLASS_SUPER(rb_find_defined_class_by_owner(data->me->defined_class,
data->me->def->body.alias.original_me->owner));
mid = data->me->def->body.alias.original_me->def->original_id;
}
else {
super_class = RCLASS_SUPER(RCLASS_ORIGIN(iclass));
mid = data->me->def->original_id;
}
if (!super_class) return Qnil;
me = (rb_method_entry_t *)rb_callable_method_entry_with_refinements(super_class, mid, &iclass);
if (!me) return Qnil;
return mnew_internal(me, me->owner, iclass, data->recv, mid, rb_obj_class(method), FALSE, FALSE);
}
Gibt eine Method der Oberklasse zurück, die aufgerufen würde, wenn super verwendet wird, oder nil, wenn es keine Methode in der Oberklasse gibt.
Gibt eine menschenlesbare Beschreibung der zugrunde liegenden Methode zurück.
"cat".method(:count).inspect #=> "#<Method: String#count(*)>" (1..3).method(:map).inspect #=> "#<Method: Range(Enumerable)#map()>"
In letzterem Fall enthält die Methodenspezifikation den "Besitzer" der ursprünglichen Methode (Enumerable-Modul, das in Range aufgenommen wird).
inspect bietet auch, wenn möglich, Methodensignatur (Aufrufsequenz) und Quellcode-Speicherort.
require 'net/http' Net::HTTP.method(:get).inspect #=> "#<Method: Net::HTTP.get(uri_or_host, path=..., port=...) <skip>/lib/ruby/2.7.0/net/http.rb:457>"
... in der Argumentdefinition bedeutet, dass das Argument optional ist (einen Standardwert hat).
Für in C definierte Methoden (Sprachkern und Erweiterungen) können Speicherort und Argumentnamen nicht extrahiert werden, und es werden nur generische Informationen in Form von * (beliebige Anzahl von Argumenten) oder _ (ein Positionsargument) bereitgestellt.
"cat".method(:count).inspect #=> "#<Method: String#count(*)>" "cat".method(:+).inspect #=> "#<Method: String#+(_)>""