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 |