如何在 perl6 中存储对父对象的引用(从 perl5 转换而来)
How to store a reference to parent object in perl6 (conversion from perl5)
我正在尝试为 RPC 服务器创建一个 Perl 6 客户端接口,class 层次结构与服务器 URL 相匹配。例如
# for url path: /account/login
# client code:
$client.account.login;
为此,'child' 对象(帐户)需要存储对其父对象(客户端)的引用。
这是我试过的:
#!/usr/bin/env perl6
use v6;
class MyApp::Client::Account {
has $!client;
method login() {
# fake login
$!client.session_id = 'abc';
return 'ok';
}
}
class MyApp::Client {
has $.session_id is rw;
method account() {
state $empire = MyApp::Client::Account.new( :client(self) );
return $empire;
}
}
use Test;
plan( 2 );
my $client = MyApp::Client.new;
my $response = $client.account.login;
is( $response, 'ok', 'login successful' );
ok( $client.session_id, 'client has session_id' );
运行 这会给出以下错误消息:
1..2
Method 'session_id' not found for invocant of class 'Any'
in method login at test.pl6 line 9
in block <unit> at test.pl6 line 29
# Looks like you planned 2 tests, but ran 0
我真的不知道任何 perl6 class/object 习语 - 我是否以正确的方式进行设计?
如果是这样,为什么 login()
方法中的 $!client
未定义?
作为参考,这是我尝试转换的 perl5(基本)版本:
#!/usr/bin/env perl
package MyApp::Client::Account;
sub new {
my $class = shift;
return bless {@_}, $class;
}
sub login {
my $self = shift;
# fake login
$self->{client}->session_id( 'abc' );
return 'ok';
}
package MyApp::Client;
sub new {
my $class = shift;
return bless {@_}, $class;
}
sub session_id {
my $self = shift;
if (@_) {
$self->{session_id} = shift;
}
return $self->{session_id};
}
sub account {
my $self = shift;
$self->{account} ||= MyApp::Client::Account->new( client => $self );
return $self->{account};
}
package main;
use Test::More tests => 2;
my $client = MyApp::Client->new;
my $response = $client->account->login;
is( $response, 'ok', 'login successful' );
ok( $client->session_id, 'client has session_id' );
这给出了预期的输出:
1..2
ok 1 - login successful
ok 2 - client has session_id
因此,Perl 6 OO 在某些方面与我使用的其他实现不同。一种是它会 auto-fill 你的成员变量的绝妙方式。但是,这仅在使用 public 访问器定义时才有效。
class G {
has $!g;
has $.e;
method emit { say (defined $!g) ?? "$!g G thing" !! "nada G thing" }
}
这将导致以下行为:
> my $g = G.new( :g('unit-of-some-kind'), :e('electric') )
G.new(e => "electric")
> $g.emit
nada G thing
> $g.e
electric
因此,当您将 self
作为对 MyApp::Client::Account
的引用传递时,它不会绑定到 $!client
变量,因为默认构造函数只会绑定到 public可访问的成员变量。
您可以选择使其可访问,也可以将对象构造逻辑掌握在自己手中。这就是我想象代码的样子,如果我需要我自己的 Perl 6 版本但必须保持客户端私有:
class MyApp::Client::Account {
has $!client;
method new(:$client) {
self.bless( :$client );
}
# binds $!client to $client automatically based on the signature
submethod BUILD(:$!client) { }
method login() {
# fake login
$!client.session_id = 'abc';
return 'ok';
}
}
class MyApp::Client {
has $.session_id is rw;
has $.account;
# the default .new will call .bless for us, which will run this BUILD
submethod BUILD {
$!account = MyApp::Client::Account.new( :client(self) );
}
}
可能需要一些时间来适应 new
与 BUILD
的区别。一个关键的区别点是 self
在 new
范围内不可用,但 在 BUILD
范围内可用 (尽管在not-yet-fully-constructed 表格)。
我正在尝试为 RPC 服务器创建一个 Perl 6 客户端接口,class 层次结构与服务器 URL 相匹配。例如
# for url path: /account/login
# client code:
$client.account.login;
为此,'child' 对象(帐户)需要存储对其父对象(客户端)的引用。
这是我试过的:
#!/usr/bin/env perl6
use v6;
class MyApp::Client::Account {
has $!client;
method login() {
# fake login
$!client.session_id = 'abc';
return 'ok';
}
}
class MyApp::Client {
has $.session_id is rw;
method account() {
state $empire = MyApp::Client::Account.new( :client(self) );
return $empire;
}
}
use Test;
plan( 2 );
my $client = MyApp::Client.new;
my $response = $client.account.login;
is( $response, 'ok', 'login successful' );
ok( $client.session_id, 'client has session_id' );
运行 这会给出以下错误消息:
1..2
Method 'session_id' not found for invocant of class 'Any'
in method login at test.pl6 line 9
in block <unit> at test.pl6 line 29
# Looks like you planned 2 tests, but ran 0
我真的不知道任何 perl6 class/object 习语 - 我是否以正确的方式进行设计?
如果是这样,为什么 login()
方法中的 $!client
未定义?
作为参考,这是我尝试转换的 perl5(基本)版本:
#!/usr/bin/env perl
package MyApp::Client::Account;
sub new {
my $class = shift;
return bless {@_}, $class;
}
sub login {
my $self = shift;
# fake login
$self->{client}->session_id( 'abc' );
return 'ok';
}
package MyApp::Client;
sub new {
my $class = shift;
return bless {@_}, $class;
}
sub session_id {
my $self = shift;
if (@_) {
$self->{session_id} = shift;
}
return $self->{session_id};
}
sub account {
my $self = shift;
$self->{account} ||= MyApp::Client::Account->new( client => $self );
return $self->{account};
}
package main;
use Test::More tests => 2;
my $client = MyApp::Client->new;
my $response = $client->account->login;
is( $response, 'ok', 'login successful' );
ok( $client->session_id, 'client has session_id' );
这给出了预期的输出:
1..2
ok 1 - login successful
ok 2 - client has session_id
因此,Perl 6 OO 在某些方面与我使用的其他实现不同。一种是它会 auto-fill 你的成员变量的绝妙方式。但是,这仅在使用 public 访问器定义时才有效。
class G {
has $!g;
has $.e;
method emit { say (defined $!g) ?? "$!g G thing" !! "nada G thing" }
}
这将导致以下行为:
> my $g = G.new( :g('unit-of-some-kind'), :e('electric') )
G.new(e => "electric")
> $g.emit
nada G thing
> $g.e
electric
因此,当您将 self
作为对 MyApp::Client::Account
的引用传递时,它不会绑定到 $!client
变量,因为默认构造函数只会绑定到 public可访问的成员变量。
您可以选择使其可访问,也可以将对象构造逻辑掌握在自己手中。这就是我想象代码的样子,如果我需要我自己的 Perl 6 版本但必须保持客户端私有:
class MyApp::Client::Account {
has $!client;
method new(:$client) {
self.bless( :$client );
}
# binds $!client to $client automatically based on the signature
submethod BUILD(:$!client) { }
method login() {
# fake login
$!client.session_id = 'abc';
return 'ok';
}
}
class MyApp::Client {
has $.session_id is rw;
has $.account;
# the default .new will call .bless for us, which will run this BUILD
submethod BUILD {
$!account = MyApp::Client::Account.new( :client(self) );
}
}
可能需要一些时间来适应 new
与 BUILD
的区别。一个关键的区别点是 self
在 new
范围内不可用,但 在 BUILD
范围内可用 (尽管在not-yet-fully-constructed 表格)。