1package RPC::ExtDirect::EventProvider; 2 3use strict; 4use warnings; 5no warnings 'uninitialized'; ## no critic 6 7use RPC::ExtDirect::Util::Accessor; 8use RPC::ExtDirect::Config; 9use RPC::ExtDirect; 10use RPC::ExtDirect::NoEvents; 11 12### PACKAGE GLOBAL VARIABLE ### 13# 14# Turn this on for debugging. 15# 16# DEPRECATED. Use `debug_eventprovider` Config option instead. 17# See RPC::ExtDirect::Config. 18# 19 20our $DEBUG; 21 22### PACKAGE GLOBAL VARIABLE ### 23# 24# Set Serializer class name so it could be configured 25# 26# DEPRECATED. Use `serializer_class_eventprovider` or `serializer_class` 27# Config options instead. 28# 29 30our $SERIALIZER_CLASS; 31 32### PACKAGE GLOBAL VARIABLE ### 33# 34# DEPRECATED. This option did nothing in previous versions of 35# RPC::ExtDirect library, and is ignored in 3.x+ 36# 37 38our $EVENT_CLASS; 39 40### PACKAGE GLOBAL VARIABLE ### 41# 42# Set Request class name so it could be configured 43# 44# DEPRECATED. Use `request_class_eventprovider` Config option instead. 45# 46 47our $REQUEST_CLASS; 48 49### PUBLIC CLASS METHOD (CONSTRUCTOR) ### 50# 51# Create a new EventProvider object with default API and Config 52# 53 54sub new { 55 my ($class, %arg) = @_; 56 57 $arg{config} ||= RPC::ExtDirect::Config->new(); 58 $arg{api} ||= RPC::ExtDirect->get_api(); 59 60 return bless { %arg }, $class; 61} 62 63### PUBLIC CLASS/INSTANCE METHOD ### 64# 65# Run all poll handlers in succession, collect the Events returned 66# by them and return serialized representation suitable for passing 67# on to client side. 68# 69# Note that the preferred way to call this method is on the EventProvider 70# object instance, but we support the class-based way for backwards 71# compatibility. 72# 73# Be aware that the only supported way to configure the EventProvider 74# is to pass a Config object to the constructor and then call poll() 75# on the instance. 76# 77 78sub poll { 79 my ($class, $env) = @_; 80 81 my $self = ref($class) ? $class : $class->new(); 82 83 my @poll_requests = $self->_get_poll_requests(); 84 85 # Even if we have nothing to poll, we must return a stub Event 86 # or client side will throw an unhandled JavaScript exception 87 return $self->_no_events unless @poll_requests; 88 89 # Run all the requests and collect their results 90 my @results = $self->_run_requests($env, \@poll_requests); 91 92 # No events returned by the handlers? We still gotta return something. 93 return $self->_no_events unless @results; 94 95 # Polling results are always JSON; no content type needed 96 my $serialized = $self->_serialize_results(@results); 97 98 # And if serialization fails we have to return something positive 99 return $serialized || $self->_no_events; 100} 101 102### PUBLIC INSTANCE METHODS ### 103# 104# Simple read-write accessors 105# 106 107RPC::ExtDirect::Util::Accessor::mk_accessors( 108 simple => [qw/ api config /], 109); 110 111############## PRIVATE METHODS BELOW ############## 112 113### PRIVATE INSTANCE METHOD ### 114# 115# Return a list of Request::PollHandler objects 116# 117 118sub _get_poll_requests { 119 my ($self) = @_; 120 121 # Compile the list of poll handler Methods 122 my @handlers = $self->api->get_poll_handlers(); 123 124 # Now create the corresponding Request objects 125 my @poll_requests; 126 for my $handler ( @handlers ) { 127 my $req = $self->_create_request($handler); 128 129 push @poll_requests, $req if $req; 130 }; 131 132 return @poll_requests; 133} 134 135### PRIVATE INSTANCE METHOD ### 136# 137# Create Request off a poll handler 138# 139 140sub _create_request { 141 my ($self, $handler) = @_; 142 143 my $config = $self->config; 144 my $api = $self->api; 145 my $action_name = $handler->action; 146 my $method_name = $handler->name; 147 148 my $request_class = $config->request_class_eventprovider; 149 150 eval "require $request_class"; 151 152 my $req = $request_class->new({ 153 config => $config, 154 api => $api, 155 action => $action_name, 156 method => $method_name, 157 }); 158 159 return $req; 160} 161 162### PRIVATE INSTANCE METHOD ### 163# 164# Run poll requests and collect results 165# 166 167sub _run_requests { 168 my ($self, $env, $requests) = @_; 169 170 # Run the requests 171 $_->run($env) for @$requests; 172 173 # Collect responses 174 my @results = map { $_->result } @$requests; 175 176 return @results; 177} 178 179### PRIVATE CLASS METHOD ### 180# 181# Serialize results 182# 183 184sub _serialize_results { 185 my ($self, @results) = @_; 186 187 # Fortunately, client side does understand more than on event 188 # batched as array 189 my $final_result = @results > 1 ? [ @results ] 190 : $results[0] 191 ; 192 193 my $config = $self->config; 194 my $api = $self->api; 195 my $debug = $config->debug_eventprovider; 196 197 my $serializer_class = $config->serializer_class_eventprovider; 198 199 eval "require $serializer_class"; 200 201 my $serializer = $serializer_class->new( 202 config => $config, 203 api => $api, 204 ); 205 206 my $json = eval { 207 $serializer->serialize( 208 mute_exceptions => 1, 209 debug => $debug, 210 data => [$final_result], 211 ) 212 }; 213 214 return $json; 215} 216 217### PRIVATE CLASS METHOD ### 218# 219# Serializes and returns a NoEvents object. 220# 221 222sub _no_events { 223 my ($self) = @_; 224 225 my $config = $self->config; 226 my $api = $self->api; 227 my $debug = $config->debug_eventprovider; 228 229 my $serializer_class = $config->serializer_class_eventprovider; 230 231 eval "require $serializer_class"; 232 233 my $serializer = $serializer_class->new( 234 config => $config, 235 api => $api, 236 ); 237 238 my $no_events = RPC::ExtDirect::NoEvents->new(); 239 my $result = $no_events->result(); 240 241 # NoEvents result can't blow up, hence no eval 242 my $serialized = $serializer->serialize( 243 mute_exceptions => !1, 244 debug => $debug, 245 data => [$result], 246 ); 247 248 return $serialized; 249} 250 2511; 252