lib/ionian/socket.rb in ionian-0.5.0 vs lib/ionian/socket.rb in ionian-0.6.0
- old
+ new
@@ -1,12 +1,15 @@
require 'ionian/extension/socket'
module Ionian
- # A convenient wrapper for TCP, UDP, and Unix sockets.
+ # A convenient wrapper for TCP, UDP, and Unix client sockets.
class Socket
+ attr_accessor :expression
+ # Creates a new socket or wraps an existing socket.
+ #
# Args:
# host: IP or hostname to connect to.
# port: Connection's port number. Default is 23. Unused by :unix protocol.
# protocol: Type of socket to create. :tcp, :udp, :unix. Default is :tcp.
# :udp will be automatically selected for addresses in the multicast range.
@@ -18,41 +21,74 @@
# reuse_addr: Set true to enable the SO_REUSEADDR flag. Allows local address reuse.
# no_delay: Set true to enable the TCP_NODELAY flag. Disables Nagle algorithm.
# cork: Set true to enable the TCP_CORK flag. Buffers multiple writes
# into one segment.
# expression: Overrides the #read_match regular expression for received data.
- def initialize(**kwargs)
- @socket = nil
+ def initialize(existing_socket = nil, **kwargs)
+ @socket = existing_socket
- # TODO: Should be able to parse the port out of host.
- # :port should override this parsed value.
+ @ionian_listeners = []
- @host = kwargs.fetch :host
- @port = kwargs.fetch :port, 23
- @bind_port = kwargs.fetch :bind_port, @port
+ @expression = kwargs.fetch :expression, nil
- # Automatically select UDP for the multicast range. Otherwise default to TCP.
- default_protocol = :tcp
- default_protocol = :udp if Ionian::Extension::Socket.multicast? @host
- default_protocol = :unix if @host.start_with? '/'
+ if existing_socket
+ # Convert existing socket.
+ @socket.extend Ionian::Extension::IO
+ @socket.extend Ionian::Extension::Socket
+
+ if existing_socket.is_a? UNIXSocket
+ @host = existing_socket.path
+ @port = nil
+ else
+ @host = existing_socket.remote_address.ip_address if existing_socket
+ @port = existing_socket.remote_address.ip_port if existing_socket
+ end
- @protocol = kwargs.fetch :protocol, default_protocol
- @persistent = kwargs.fetch :persistent, true
- @expression = kwargs.fetch :expression, nil
+ if @socket.is_a? TCPSocket
+ @protocol = :tcp
+ elsif @socket.is_a? UDPSocket
+ @protocol = :udp
+ elsif @socket.is_a? UNIXSocket
+ @protocol = :unix
+ end
+
+ @persistent = true # Existing sockets are always persistent.
+
+ @socket.expression = @expression if @expression
+
+ initialize_socket_methods
+ else
+ # Initialize new socket.
+
+ # TODO: Should be able to parse the port out of host.
+ # :port should override this parsed value.
+
+ @host = kwargs.fetch :host
+ @port = kwargs.fetch :port, 23
+ @bind_port = kwargs.fetch :bind_port, @port
- @reuse_addr = kwargs.fetch :reuse_addr, false
- @no_delay = kwargs.fetch :no_delay, false
- @cork = kwargs.fetch :cork, false
+ # Automatically select UDP for the multicast range. Otherwise default to TCP.
+ default_protocol = :tcp
+ default_protocol = :udp if Ionian::Extension::Socket.multicast? @host
+ default_protocol = :unix if @host.start_with? '/'
+
+ @protocol = kwargs.fetch :protocol, default_protocol
+ @persistent = kwargs.fetch :persistent, true
+
+ @reuse_addr = kwargs.fetch :reuse_addr, false
+ @no_delay = kwargs.fetch :no_delay, false
+ @cork = kwargs.fetch :cork, false
- create_socket if @persistent
+
+ create_socket if @persistent
+ end
end
# Returns a symbol of the type of protocol this socket uses:
# :tcp, :udp, :unix
- def protocol?
- @protocol
- end
+ attr_reader :protocol
+ alias_method :protocol?, :protocol
# Returns true if the socket remains open after writing data.
def persistent?
@persistent == false || @persistent == nil ? false : true
end
@@ -76,11 +112,29 @@
@socket.close unless @persistent
matches
end
+ # Register a block to be called when #run_match receives matched data.
+ # Method callbacks can be registered with &object.method(:method).
+ # Returns a reference to the given block.
+ # block = ionian_socket.register_observer {...}
+ def register_observer &block
+ @ionian_listeners << block unless @ionian_listeners.include? block
+ @socket.register_observer &block if @socket
+ block
+ end
+ alias_method :on_match, :register_observer
+
+ # Unregister a block from being called when matched data is received.
+ def unregister_observer(&block)
+ @ionian_listeners.delete_if {|o| o == block}
+ @socket.unregister_observer &block if @socket
+ block
+ end
+
### Methods Forwarded To @socket ###
# Returns true if there is data in the receive buffer.
# Args:
# Timeout: Number of seconds to wait for data until
@@ -143,10 +197,11 @@
case @protocol
when :tcp
@socket = ::TCPSocket.new @host, @port
@socket.extend Ionian::Extension::Socket
+ @socket.expression = @expression if @expression
@socket.no_delay = true if @no_delay
@socket.cork = true if @cork
when :udp
@socket = ::UDPSocket.new
@@ -167,9 +222,12 @@
# TODO: Implement SO_LINGER flag for non-persistent sockets;
# especially send-and-forget.
@socket.expression = @expression if @expression
+
+ # Register listeners.
+ @ionian_listeners.each { |proc| @socket.on_match &proc }
initialize_socket_methods
end
# Expose the @socket methods that haven't been defined by this class.
\ No newline at end of file