lib/plezi/handlers/controller_magic.rb in plezi-0.8.5 vs lib/plezi/handlers/controller_magic.rb in plezi-0.8.6

- old
+ new

@@ -42,20 +42,27 @@ attr_reader :flash # the parameters used to create the host (the parameters passed to the `listen` / `add_service` call). attr_reader :host_params - # a unique UUID to identify the object - used to make sure Radis broadcasts don't triger the + # Get's the websocket's unique identifier for unicast transmissions. + # + # This UUID is also used to make sure Radis broadcasts don't triger the # boadcasting object's event. - attr_reader :uuid + def uuid + @uuid ||= SecureRandom.uuid + end + alias :unicast_id :uuid # checks whether this instance accepts broadcasts (WebSocket instances). def accepts_broadcast? @_accepts_broadcast end # sets the controller to refuse "broadcasts". # + # The controller will also refuse any messages directed at it using the `unicast` method. + # # This allows some websocket connections to isolate themselves even before they are fully disconnected. # # call this method once it is clear the websocket connection should be terminated. def refuse_broadcasts @_accepts_broadcast = false @@ -251,13 +258,18 @@ # the method will be called asynchrnously for each sibling instance of this Controller class. # def broadcast method_name, *args, &block return false unless self.class.public_instance_methods.include?(method_name) @uuid ||= SecureRandom.uuid - self.class.__inner_redis_broadcast(uuid, method_name, args, &block) || self.class.__inner_process_broadcast(uuid, method_name.to_sym, args, &block) + self.class.__inner_redis_broadcast(uuid, nil, method_name, args, &block) || self.class.__inner_process_broadcast(uuid, nil, method_name.to_sym, args, &block) end + # {include:ControllerMagic::ClassMethods#unicast} + def unicast target_uuid, method_name, *args + self.class.unicast target_uuid, method_name, *args + end + # WebSockets. # # Use this to collect data from all 'sibling' websockets (websockets that have been created using the same Controller class). # # This method will call the requested method on all instance siblings and return an Array of the returned values (including nil values). @@ -368,13 +380,13 @@ # available_routing_methods # available_public_methods # end - # reviews the Redis connection, sets it up if it's missing and returns the Redis connection. + # Reviews the Redis connection, sets it up if it's missing and returns the Redis connection. # - # a Redis connection will be automatically created if the `ENV['PL_REDIS_URL']` is set. + # A Redis connection will be automatically created if the `ENV['PL_REDIS_URL']` is set. # for example: # ENV['PL_REDIS_URL'] = ENV['REDISCLOUD_URL']` # or # ENV['PL_REDIS_URL'] = "redis://username:password@my.host:6379" def redis_connection @@ -407,11 +419,11 @@ begin Redis.new(host: @@redis_uri.host, port: @@redis_uri.port, password: @@redis_uri.password).subscribe(redis_channel_name) do |on| on.message do |channel, msg| args = JSON.parse(msg) params = args.shift - __inner_process_broadcast params['_pl_ignore_object'], params['_pl_method_broadcasted'].to_sym, args + __inner_process_broadcast params['_pl_ignore_object'], params['_pl_target_object'], params['_pl_method_broadcasted'].to_sym, args end end rescue Exception => e Plezi.error e retry @@ -425,20 +437,20 @@ def redis_channel_name self.superclass.name.to_s end # broadcasts messages (methods) for this process - def __inner_process_broadcast ignore, method_name, args, &block - ObjectSpace.each_object(self) { |controller| Plezi.callback controller, method_name, *args, &block if controller.accepts_broadcast? && (!ignore || controller.uuid != ignore) } + def __inner_process_broadcast ignore, target, method_name, args, &block + ObjectSpace.each_object(self) { |controller| Plezi.callback controller, method_name, *args, &block if controller.accepts_broadcast? && (!ignore || (controller.uuid != ignore)) && (!target || (controller.uuid == target)) } end # broadcasts messages (methods) between all processes (using Redis). - def __inner_redis_broadcast ignore, method_name, args, &block + def __inner_redis_broadcast ignore, target, method_name, args, &block return false unless redis_connection raise "Radis broadcasts cannot accept blocks (no inter-process callbacks of memory sharing)!" if block # raise "Radis broadcasts accept only one paramater, which is an optional Hash (no inter-process memory sharing)" if args.length > 1 || (args[0] && !args[0].is_a?(Hash)) - args.unshift ({_pl_method_broadcasted: method_name, _pl_ignore_object: ignore}) + args.unshift ({_pl_method_broadcasted: method_name, _pl_ignore_object: ignore, _pl_target_object: target}) redis_connection.publish(redis_channel_name, args.to_json ) true end # WebSockets. @@ -454,10 +466,25 @@ # this method accepts and optional block (NON-REDIS ONLY) to be used as a callback for each sibling's event. # # the method will be called asynchrnously for each sibling instance of this Controller class. def broadcast method_name, *args, &block return false unless public_instance_methods.include?(method_name) - __inner_redis_broadcast(nil, method_name, args, &block) || __inner_process_broadcast(nil, method_name.to_sym, args, &block) + __inner_redis_broadcast(nil, nil, method_name, args, &block) || __inner_process_broadcast(nil, nil, method_name.to_sym, args, &block) + end + + # WebSockets. + # + # Class method. + # + # Use this to unidcast an event to specific websocket connection using it's UUID. + # + # accepts: + # target_uuid:: the target's unique UUID. + # method_name:: a Symbol with the method's name that should respond to the broadcast. + # *args:: any arguments that should be passed to the method (IF REDIS IS USED, LIMITATIONS APPLY). + def unicast target_uuid, method_name, *args + return false unless public_instance_methods.include?(method_name.to_sym) + __inner_redis_broadcast(nil, target_uuid, method_name, args) || __inner_process_broadcast(nil, target_uuid, method_name.to_sym, args) end # WebSockets. # # Class method.