2016-01-22 19 views
3

我試圖創建一個Perl客戶機接口到一個RPC服務器,並且類層次結構與服務器URL匹配。例如如何在perl6中存儲對父對象的引用(從perl5轉換)

# for url path: /account/login 
# client code: 
$client.account.login; 

對於這項工作,將「孩子」對象(賬戶)需要存儲到它的父對象(客戶端)的參考。
這是我已經試過:

#!/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類/對象成語,但 - 我是甚至將關於正確方式的設計?
如果是這樣,爲什麼$!client內的login()方法未定義?

僅供參考,下面是我試圖從轉換的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 

回答

5

所以有幾個Perl 6 OO與我用過的其他實現不同。一種是它會自動填充你的成員變量的真棒方法。但是,這隻有在使用公共訪問者定義時纔有效。

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變量,因爲默認的構造函數只會綁定到公共可訪問的成員變量。

您可以選擇使其可訪問,也可以將對象構建邏輯放到您自己的手中。這就是我想象中的代碼看起來是我需要在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)); 
    } 
} 

它可能需要一些時間來適應的newBUILD區別。一個關鍵的區別點是selfnew的範圍內不可用,但它的可用於BUILD(儘管處於尚未完全構造的形式)的範圍。

相關問題