Mercurial > pub > Impl
comparison lib/IMPL/Object/EventSource.pm @ 407:c6e90e02dd17 ref20150831
renamed Lib->lib
| author | cin | 
|---|---|
| date | Fri, 04 Sep 2015 19:40:23 +0300 | 
| parents | |
| children | 
   comparison
  equal
  deleted
  inserted
  replaced
| 406:f23fcb19d3c1 | 407:c6e90e02dd17 | 
|---|---|
| 1 package IMPL::Object::EventSource; | |
| 2 use strict; | |
| 3 require IMPL::Exception; | |
| 4 use IMPL::Class::Property; | |
| 5 | |
| 6 sub CreateEvent { | |
| 7 my ($class,$event) = @_; | |
| 8 | |
| 9 die new IMPL::Exception('A name is required for the event') unless $event; | |
| 10 | |
| 11 (my $fullEventName = "$class$event") =~ s/:://g; | |
| 12 | |
| 13 my $globalEventTable = new IMPL::Object::EventSource::EventTable($fullEventName); | |
| 14 my $propEventTable = $event.'Table'; | |
| 15 public CreateProperty($class,$propEventTable,prop_all); | |
| 16 public CreateProperty($class,$event, | |
| 17 { | |
| 18 get => sub { | |
| 19 my $this = shift; | |
| 20 if (not defined wantarray and caller(1) eq $class) { | |
| 21 (ref $this ? $this->$propEventTable() || $globalEventTable : $globalEventTable)->Invoke($this); | |
| 22 } else { | |
| 23 if (ref $this) { | |
| 24 if (my $table = $this->$propEventTable()) { | |
| 25 return $table; | |
| 26 } else { | |
| 27 $table = new IMPL::Object::EventSource::EventTable($fullEventName,$globalEventTable); | |
| 28 $this->$propEventTable($table); | |
| 29 return $table; | |
| 30 } | |
| 31 } else { | |
| 32 return $globalEventTable; | |
| 33 } | |
| 34 } | |
| 35 }, | |
| 36 set => sub { | |
| 37 (ref $_[0] ? $_[0]->$propEventTable() || $globalEventTable : $globalEventTable)->Invoke(@_); | |
| 38 } | |
| 39 } | |
| 40 ); | |
| 41 } | |
| 42 | |
| 43 sub CreateStaticEvent { | |
| 44 my ($class,$event) = @_; | |
| 45 | |
| 46 die new IMPL::Exception('A name is required for the event') unless $event; | |
| 47 | |
| 48 (my $fullEventName = "$class$event") =~ s/:://g; | |
| 49 | |
| 50 my $globalEventTable = new IMPL::Object::EventSource::EventTable($fullEventName); | |
| 51 | |
| 52 no strict 'refs'; | |
| 53 *{"${class}::$event"} = sub { | |
| 54 shift; | |
| 55 if (not @_) { | |
| 56 if (not defined wantarray and caller(1) eq $class) { | |
| 57 $globalEventTable->Invoke($class); | |
| 58 } else { | |
| 59 return $globalEventTable; | |
| 60 } | |
| 61 } else { | |
| 62 $globalEventTable->Invoke($class,@_); | |
| 63 } | |
| 64 }; | |
| 65 } | |
| 66 | |
| 67 package IMPL::Object::EventSource::EventTable; | |
| 68 use parent qw(IMPL::Object); | |
| 69 use IMPL::Class::Property; | |
| 70 use Scalar::Util qw(weaken); | |
| 71 | |
| 72 use overload | |
| 73 '+=' => \&opSubscribe, | |
| 74 'fallback' => 1; | |
| 75 | |
| 76 BEGIN { | |
| 77 public _direct property Name => prop_get; | |
| 78 public _direct property Handlers => { get => \&get_handlers }; | |
| 79 private _direct property Next => prop_all; | |
| 80 private _direct property NextId => prop_all; | |
| 81 } | |
| 82 | |
| 83 sub CTOR { | |
| 84 my $this = shift; | |
| 85 | |
| 86 $this->{$Handlers} = {}; | |
| 87 $this->{$Name} = shift; | |
| 88 $this->{$Next} = shift; | |
| 89 $this->{$NextId} = 1; | |
| 90 } | |
| 91 | |
| 92 sub get_handlers { | |
| 93 my $this = shift; | |
| 94 return values %{$this->{$Handlers}}; | |
| 95 } | |
| 96 | |
| 97 sub Invoke { | |
| 98 my $this = shift; | |
| 99 | |
| 100 my $tmp; | |
| 101 $tmp = $_ and local($_) or &$tmp(@_) foreach values %{$this->{$Handlers}}; | |
| 102 | |
| 103 $this->{$Next}->Invoke(@_) if $this->{$Next}; | |
| 104 } | |
| 105 | |
| 106 sub Subscribe { | |
| 107 my ($this,$consumer,$nameHandler) = @_; | |
| 108 | |
| 109 my $id = $this->{$NextId} ++; | |
| 110 | |
| 111 if (ref $consumer eq 'CODE') { | |
| 112 $this->{$Handlers}{$id} = $consumer; | |
| 113 } else { | |
| 114 $nameHandler ||= $this->Name or die new IMPL::Exception('The name for the event handler method must be specified'); | |
| 115 my $method = $consumer->can($nameHandler) or die new IMPL::Exception('Can\'t find the event handler method',$nameHandler,$consumer); | |
| 116 | |
| 117 weaken($consumer) if ref $consumer; | |
| 118 $this->{$Handlers}{$id} = sub { | |
| 119 unshift @_, $consumer; | |
| 120 $consumer ? goto &$method : delete $this->{$Handlers}{$id}; | |
| 121 }; | |
| 122 } | |
| 123 | |
| 124 return $id; | |
| 125 } | |
| 126 | |
| 127 sub Remove { | |
| 128 my ($this,$id) = @_; | |
| 129 return delete $this->{$Handlers}{$id}; | |
| 130 } | |
| 131 1; | |
| 132 | |
| 133 __END__ | |
| 134 =pod | |
| 135 =head1 SYNOPSIS | |
| 136 package Foo; | |
| 137 use parent qw(IMPL::Object IMPL::Object::EventSource); | |
| 138 | |
| 139 # declare events | |
| 140 __PACKAGE__->CreateEvent('OnUpdate'); | |
| 141 __PACKAGE__->CreateStaticEvent('OnNewObject'); | |
| 142 | |
| 143 sub CTOR { | |
| 144 my $this = shift; | |
| 145 // rise static event | |
| 146 $this->OnNewObject(); | |
| 147 } | |
| 148 | |
| 149 sub Update { | |
| 150 my ($this,$val) = @_; | |
| 151 | |
| 152 // rise object event | |
| 153 $this->OnUpdate($val); | |
| 154 } | |
| 155 | |
| 156 package Bar; | |
| 157 | |
| 158 // subscribe static event | |
| 159 Foo->OnNewObject->Subscribe(sub { warn "New $_[0] created" } ); | |
| 160 | |
| 161 sub LookForFoo { | |
| 162 my ($this,$foo) = @_; | |
| 163 | |
| 164 // subscribe object event | |
| 165 $foo->OnUpdate->Subscribe($this,'OnFooUpdate'); | |
| 166 } | |
| 167 | |
| 168 // event handler | |
| 169 sub OnFooUpdate { | |
| 170 my ($this,$sender,$value) = @_; | |
| 171 } | |
| 172 | |
| 173 =head1 DESCRIPTION | |
| 174 Позволяет объявлять и инициировать события. События делятся на статические и | |
| 175 локальные. Статические события объявляются для класса и при возникновении | |
| 176 данного события вызываются всегда все подписчики. Статические события могут быть | |
| 177 вызваны как для класса, так и для объекта, что приведет к одинаковым результатам. | |
| 178 | |
| 179 Локальные события состоят из статической (как статические события) и локальной | |
| 180 части. Если подписываться на события класса, то обработчики будут вызываться при | |
| 181 любых вариантах инициации данного события (как у статических событий). При | |
| 182 подписке на события объекта, обработчик будет вызван только при возникновении | |
| 183 событий у данного объекта. | |
| 184 | |
| 185 =head1 METHODS | |
| 186 =level 4 | |
| 187 =back | |
| 188 | |
| 189 =head1 EventTable | |
| 190 | |
| 191 =cut | 
