unix_socks 0.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGES.md +5 -0
- data/Gemfile +5 -0
- data/LICENSE +19 -0
- data/README.md +118 -0
- data/Rakefile +42 -0
- data/lib/unix_socks/message.rb +30 -0
- data/lib/unix_socks/server.rb +126 -0
- data/lib/unix_socks/version.rb +8 -0
- data/lib/unix_socks.rb +14 -0
- data/spec/spec_helper.rb +12 -0
- data/spec/unix_socks/message_spec.rb +38 -0
- data/spec/unix_socks/server_spec.rb +140 -0
- data/unix_socks.gemspec +34 -0
- metadata +183 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: e300523dc5acc1d27d11f2179a7beba1e5f5ee2ae82cba1a92de33747bc94c07
|
4
|
+
data.tar.gz: 033f4aae216f6c5c8d31df5c7df136167e9169aa8729f200081b6bf636003e5a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 551193b2e23226c7f3434811d679b8df9f296b69a1c9a4639327db8110cf1870095589c352b35c5a12b7e423023f07de0f088a8f52ce4fa62e1d77d934c4b47e
|
7
|
+
data.tar.gz: 5b7e2c93ff081a4794ccf2dd94bb8f9181da4d2d1104b79d4051253f5c36948046615e000c858e8d360d4894871eee5a366687a72286b321176a7e3078a58e89
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Copyright Florian Frank
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
4
|
+
this software and associated documentation files (the “Software”), to deal in
|
5
|
+
the Software without restriction, including without limitation the rights to
|
6
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
7
|
+
of the Software, and to permit persons to whom the Software is furnished to do
|
8
|
+
so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in all
|
11
|
+
copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
19
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,118 @@
|
|
1
|
+
# UnixSocks 🧦🧦
|
2
|
+
|
3
|
+
## Description
|
4
|
+
|
5
|
+
A Ruby library for handling Unix socket-based communication.
|
6
|
+
|
7
|
+
## Features
|
8
|
+
|
9
|
+
- **Message Handling**: Simplify sending and receiving messages over Unix sockets.
|
10
|
+
- **Dynamic Method Access**: Access message body values using method names (e.g., `message.key`).
|
11
|
+
- **Background Processing**: Run the server in a background thread to avoid blocking main execution.
|
12
|
+
- **Robust Error Handling**: Gracefully handle socket disconnections and JSON parsing errors.
|
13
|
+
|
14
|
+
## Installation
|
15
|
+
|
16
|
+
Add this gem to your Gemfile:
|
17
|
+
|
18
|
+
```ruby
|
19
|
+
gem 'unix_socks'
|
20
|
+
```
|
21
|
+
|
22
|
+
And install it using Bundler:
|
23
|
+
|
24
|
+
```bash
|
25
|
+
bundle install
|
26
|
+
```
|
27
|
+
|
28
|
+
Or install the gem directly:
|
29
|
+
|
30
|
+
```bash
|
31
|
+
gem install unix_socks
|
32
|
+
```
|
33
|
+
|
34
|
+
## Usage
|
35
|
+
|
36
|
+
### 1. Server Setup
|
37
|
+
|
38
|
+
Create a server instance and start listening for connections:
|
39
|
+
|
40
|
+
```ruby
|
41
|
+
require 'unix_socks'
|
42
|
+
|
43
|
+
server = UnixSocks::Server.new(socket_name: 'my_socket')
|
44
|
+
|
45
|
+
# Run the server in the background to avoid blocking
|
46
|
+
thread = server.receive_in_background(force: true) do |message|
|
47
|
+
puts "Received message: #{message.inspect}"
|
48
|
+
end
|
49
|
+
|
50
|
+
thread.join
|
51
|
+
```
|
52
|
+
|
53
|
+
### 2. Sending Messages
|
54
|
+
|
55
|
+
Transmit messages to connected clients:
|
56
|
+
|
57
|
+
```ruby
|
58
|
+
client = UnixSocks::Server.new(socket_name: 'my_socket')
|
59
|
+
|
60
|
+
# Prepare your message
|
61
|
+
message = { status: 'success', data: [1, 2, 3] }
|
62
|
+
|
63
|
+
# Send the message
|
64
|
+
client.transmit(message)
|
65
|
+
```
|
66
|
+
|
67
|
+
### 3. Responding to Messages
|
68
|
+
|
69
|
+
Handle incoming messages and send responses:
|
70
|
+
|
71
|
+
```ruby
|
72
|
+
require 'unix_socks'
|
73
|
+
|
74
|
+
server = UnixSocks::Server.new(socket_name: 'my_socket')
|
75
|
+
|
76
|
+
def handle_message(message)
|
77
|
+
# Access message body values using method names
|
78
|
+
puts "Received status: #{message.status}"
|
79
|
+
|
80
|
+
# Send a response
|
81
|
+
message.respond({ status: 'acknowledged' })
|
82
|
+
end
|
83
|
+
|
84
|
+
# Use in your server setup
|
85
|
+
thread = server.receive_in_background(force: true) do |message|
|
86
|
+
handle_message(message)
|
87
|
+
end
|
88
|
+
|
89
|
+
thread.join
|
90
|
+
```
|
91
|
+
|
92
|
+
And in the client:
|
93
|
+
```ruby
|
94
|
+
client = UnixSocks::Server.new(socket_name: 'my_socket')
|
95
|
+
|
96
|
+
# Prepare your message
|
97
|
+
message = { status: 'success', data: [1, 2, 3] }
|
98
|
+
|
99
|
+
# Send the message
|
100
|
+
response = client.transmit_with_response(message)
|
101
|
+
|
102
|
+
# Receive the response
|
103
|
+
puts "Received server response status: #{response.status}"
|
104
|
+
```
|
105
|
+
|
106
|
+
### 4. Message Object Features
|
107
|
+
|
108
|
+
- **Dynamic Access**: Methods like `message.status` automatically map to the message body.
|
109
|
+
- **Disconnect Handling**: Safely close socket connections using `disconnect`.
|
110
|
+
- **Error Resilience**: The `respond` method handles disconnections gracefully.
|
111
|
+
|
112
|
+
## Author
|
113
|
+
|
114
|
+
[Florian Frank](mailto:flori@ping.de)
|
115
|
+
|
116
|
+
## License
|
117
|
+
|
118
|
+
[MIT License](./LICENSE)
|
data/Rakefile
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
# vim: set filetype=ruby et sw=2 ts=2:
|
2
|
+
|
3
|
+
require 'gem_hadar'
|
4
|
+
|
5
|
+
GemHadar do
|
6
|
+
name 'unix_socks'
|
7
|
+
module_type :module
|
8
|
+
author 'Florian Frank'
|
9
|
+
email 'flori@ping.de'
|
10
|
+
homepage "https://github.com/flori/#{name}"
|
11
|
+
summary <<~EOT
|
12
|
+
A Ruby library for inter-process communication via Unix sockets with
|
13
|
+
dynamic message handling
|
14
|
+
EOT
|
15
|
+
description <<~EOT
|
16
|
+
This library enables communication between processes using Unix sockets. It
|
17
|
+
handles message transmission, socket management, and cleanup, supporting
|
18
|
+
both synchronous and asynchronous operations while providing error handling
|
19
|
+
for robust development.
|
20
|
+
EOT
|
21
|
+
test_dir 'spec'
|
22
|
+
ignore '.*.sw[pon]', 'pkg', 'Gemfile.lock', '.AppleDouble', '.bundle',
|
23
|
+
'.yardoc', 'doc', 'tags', 'errors.lst', 'cscope.out', 'coverage', 'tmp',
|
24
|
+
'yard'
|
25
|
+
package_ignore '.all_images.yml', '.tool-versions', '.gitignore', 'VERSION',
|
26
|
+
'.rspec', *Dir.glob('.github/**/*', File::FNM_DOTMATCH)
|
27
|
+
readme 'README.md'
|
28
|
+
|
29
|
+
required_ruby_version '~> 3.1'
|
30
|
+
|
31
|
+
dependency 'json', '~> 2.0'
|
32
|
+
dependency 'tins', '~> 1.3'
|
33
|
+
development_dependency 'all_images', '~> 0.4'
|
34
|
+
development_dependency 'rspec', '~> 3.2'
|
35
|
+
development_dependency 'debug'
|
36
|
+
development_dependency 'simplecov'
|
37
|
+
development_dependency 'yard'
|
38
|
+
|
39
|
+
licenses << 'MIT'
|
40
|
+
|
41
|
+
clobber 'coverage'
|
42
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'tins/xt/ask_and_send'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
# Represents a message sent or received over a Unix socket, extending
|
5
|
+
# JSON::GenericObject for dynamic access to message attributes.
|
6
|
+
class UnixSocks::Message < JSON::GenericObject
|
7
|
+
# Disconnects the socket connection.
|
8
|
+
#
|
9
|
+
# Closes the underlying socket, effectively ending the communication session.
|
10
|
+
def disconnect
|
11
|
+
socket.close
|
12
|
+
end
|
13
|
+
|
14
|
+
# The respond method sends a response back to the client over the Unix socket
|
15
|
+
# connection.
|
16
|
+
#
|
17
|
+
# It first converts the provided answer into JSON format, and then writes it
|
18
|
+
# to the socket using socket.puts.
|
19
|
+
#
|
20
|
+
# @param answer [ Object ] The response to be sent back to the client.
|
21
|
+
#
|
22
|
+
# @return [ UnixSocks::Message ] The current message object.
|
23
|
+
def respond(answer)
|
24
|
+
answer = answer.ask_and_send(:to_json) or
|
25
|
+
raise TypeError, 'needs to be convertibla to JSON'
|
26
|
+
socket.puts answer
|
27
|
+
rescue Errno::EPIPE
|
28
|
+
self
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,126 @@
|
|
1
|
+
# Manages Unix socket-based communication, providing both server and client
|
2
|
+
# functionality.
|
3
|
+
class UnixSocks::Server
|
4
|
+
include FileUtils
|
5
|
+
|
6
|
+
# Initializes a new UnixSocks::Server instance.
|
7
|
+
#
|
8
|
+
# @param socket_name [ String ] The name of the server socket file.
|
9
|
+
# @param runtime_dir [ String, nil ] The path to the runtime directory where
|
10
|
+
# the server socket will be created. If not provided, it defaults to the
|
11
|
+
# value returned by #default_runtime_dir.
|
12
|
+
def initialize(socket_name:, runtime_dir: default_runtime_dir)
|
13
|
+
@socket_name, @runtime_dir = socket_name, runtime_dir
|
14
|
+
end
|
15
|
+
|
16
|
+
# Returns the default runtime directory path based on the XDG_RUNTIME_DIR
|
17
|
+
# environment variable.
|
18
|
+
#
|
19
|
+
# If the XDG_RUNTIME_DIR environment variable is set, its value is used as
|
20
|
+
# the runtime directory path. Otherwise, the default path '~/.local/run' is
|
21
|
+
# used.
|
22
|
+
#
|
23
|
+
# @return [ String ] The default runtime directory path.
|
24
|
+
def default_runtime_dir
|
25
|
+
File.expand_path(ENV.fetch('XDG_RUNTIME_DIR', '~/.local/run'))
|
26
|
+
end
|
27
|
+
|
28
|
+
# Returns the path to the server socket file.
|
29
|
+
#
|
30
|
+
# This method constructs the full path to the server socket by joining the
|
31
|
+
# runtime directory and the socket name.
|
32
|
+
#
|
33
|
+
# @return [ String ] The path to the server socket file.
|
34
|
+
def server_socket_path
|
35
|
+
File.expand_path(File.join(@runtime_dir, @socket_name))
|
36
|
+
end
|
37
|
+
|
38
|
+
# The transmit method sends a message over the Unix socket connection.
|
39
|
+
#
|
40
|
+
# It first prepares the message by converting it to JSON format, and then
|
41
|
+
# establishes a connection to the server socket using UNIXSocket.new.
|
42
|
+
#
|
43
|
+
# Finally, it writes the prepared message to the socket using socket.puts,
|
44
|
+
# ensuring that the socket is properly closed after use.
|
45
|
+
#
|
46
|
+
# @param message [ Message ] The message to be sent over the Unix socket.
|
47
|
+
def transmit(message)
|
48
|
+
mkdir_p @runtime_dir
|
49
|
+
socket = UNIXSocket.new(server_socket_path)
|
50
|
+
socket.puts JSON(message)
|
51
|
+
socket
|
52
|
+
end
|
53
|
+
|
54
|
+
# Sends a message and returns the parsed JSON response.
|
55
|
+
#
|
56
|
+
# @param message [Object] The message to be sent as JSON.
|
57
|
+
# @return [Hash, nil] The parsed JSON response or nil if parsing fails.
|
58
|
+
def transmit_with_response(message)
|
59
|
+
socket = transmit(message)
|
60
|
+
parse_json_message(socket.gets, socket)
|
61
|
+
end
|
62
|
+
|
63
|
+
# Receives messages from clients connected to the server socket.
|
64
|
+
#
|
65
|
+
# This method establishes a connection to the server socket and listens for incoming
|
66
|
+
# messages. When a message is received, it is parsed as JSON and converted into a
|
67
|
+
# UnixSocks::Message object. The block provided to this method is then called with
|
68
|
+
# the message object as an argument.
|
69
|
+
#
|
70
|
+
# If the `force` parameter is set to true, any existing server socket file will be
|
71
|
+
# overwritten without raising an error.
|
72
|
+
#
|
73
|
+
# @param force [ Boolean ] Whether to overwrite any existing server socket file.
|
74
|
+
# @yield [ UnixSocks::Message ] The received message.
|
75
|
+
def receive(force: false, &block)
|
76
|
+
mkdir_p @runtime_dir
|
77
|
+
if !force && socket_path_exist?
|
78
|
+
raise Errno::EEXIST, "Path already exists #{server_socket_path.inspect}"
|
79
|
+
end
|
80
|
+
Socket.unix_server_loop(server_socket_path) do |socket, client_addrinfo|
|
81
|
+
message = pop_message(socket) and block.(message)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# The receive_in_background method runs the server socket listener in a
|
86
|
+
# separate thread, allowing it to continue executing without blocking the
|
87
|
+
# main program flow.
|
88
|
+
#
|
89
|
+
# @param force [ Boolean ] Whether to overwrite any existing server socket file.
|
90
|
+
# @yield [ UnixSocks::Message ] The received message.
|
91
|
+
def receive_in_background(force: false, &block)
|
92
|
+
Thread.new do
|
93
|
+
receive(force:, &block)
|
94
|
+
ensure
|
95
|
+
remove_socket_path
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
# Checks if the server socket file exists.
|
100
|
+
#
|
101
|
+
# @return [ Boolean ] True if the socket file exists, false otherwise.
|
102
|
+
def socket_path_exist?
|
103
|
+
File.exist?(server_socket_path)
|
104
|
+
end
|
105
|
+
|
106
|
+
# Safely removes the server socket file from the filesystem.
|
107
|
+
def remove_socket_path
|
108
|
+
FileUtils.rm_f server_socket_path
|
109
|
+
end
|
110
|
+
|
111
|
+
private
|
112
|
+
|
113
|
+
def pop_message(socket)
|
114
|
+
parse_json_message(socket.gets, socket)
|
115
|
+
end
|
116
|
+
|
117
|
+
def parse_json_message(data, socket)
|
118
|
+
data = data.strip
|
119
|
+
data.empty? and return nil
|
120
|
+
obj = JSON.parse(data, object_class: UnixSocks::Message)
|
121
|
+
obj.socket = socket
|
122
|
+
obj
|
123
|
+
rescue JSON::ParserError => e
|
124
|
+
warn "Caught #{e.class}: #{e} for #{data[0, 512].inspect}"
|
125
|
+
end
|
126
|
+
end
|
data/lib/unix_socks.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'fileutils'
|
3
|
+
require 'socket'
|
4
|
+
require 'tins'
|
5
|
+
|
6
|
+
# Provides classes for handling inter-process communication via Unix 🧦🧦.
|
7
|
+
# Supports dynamic message handling, background processing, and robust error
|
8
|
+
# management.
|
9
|
+
module UnixSocks
|
10
|
+
end
|
11
|
+
|
12
|
+
require 'unix_socks/version'
|
13
|
+
require 'unix_socks/message'
|
14
|
+
require 'unix_socks/server'
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe UnixSocks::Message do
|
4
|
+
let(:socket) { instance_double(Socket) }
|
5
|
+
let(:key) { 'value' }
|
6
|
+
|
7
|
+
subject { described_class[key:, socket:] }
|
8
|
+
|
9
|
+
describe '#initialize' do
|
10
|
+
it 'sets the body and socket' do
|
11
|
+
expect(subject).to be_a described_class
|
12
|
+
expect(subject.key).to eq 'value'
|
13
|
+
expect(subject.socket).to eq(socket)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe '#disconnect' do
|
18
|
+
it 'closes the socket' do
|
19
|
+
expect(socket).to receive(:close)
|
20
|
+
subject.disconnect
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe '#respond' do
|
25
|
+
let(:answer) { { response: 'ok' } }
|
26
|
+
|
27
|
+
it 'sends a JSON response over the socket' do
|
28
|
+
expected_json = answer.to_json
|
29
|
+
expect(socket).to receive(:puts).with(expected_json)
|
30
|
+
subject.respond(answer)
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'handles EPIPE errors gracefully' do
|
34
|
+
allow(socket).to receive(:puts).and_raise(Errno::EPIPE)
|
35
|
+
expect { subject.respond(answer) }.not_to raise_error
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,140 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'tins/xt/expose'
|
3
|
+
|
4
|
+
describe UnixSocks::Server do
|
5
|
+
let(:socket_name) { 'test_socket' }
|
6
|
+
let(:runtime_dir) { './tmp' }
|
7
|
+
let(:server) { UnixSocks::Server.new(socket_name: socket_name, runtime_dir: runtime_dir).expose }
|
8
|
+
|
9
|
+
describe '#initialize' do
|
10
|
+
it 'sets the socket name and runtime directory' do
|
11
|
+
expect(server.instance_variable_get(:@socket_name)).to eq(socket_name)
|
12
|
+
expect(server.instance_variable_get(:@runtime_dir)).to eq(runtime_dir)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe '#default_runtime_dir' do
|
17
|
+
it 'returns the correct default runtime directory' do
|
18
|
+
ENV['XDG_RUNTIME_DIR'] = nil
|
19
|
+
expect(server.default_runtime_dir).to eq(File.expand_path('~/.local/run'))
|
20
|
+
|
21
|
+
ENV['XDG_RUNTIME_DIR'] = '/tmp/runtime'
|
22
|
+
expect(server.default_runtime_dir).to eq('/tmp/runtime')
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe '#server_socket_path' do
|
27
|
+
it 'returns the correct socket path' do
|
28
|
+
expected_path = File.expand_path(File.join(runtime_dir, socket_name))
|
29
|
+
expect(server.server_socket_path).to eq(expected_path)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe '#transmit' do
|
34
|
+
let(:message) { { test: 'message' } }
|
35
|
+
|
36
|
+
it 'sends a message over the Unix socket' do
|
37
|
+
expect(server).to receive(:mkdir_p).with(runtime_dir)
|
38
|
+
expect(UNIXSocket).to receive(:new).with(server.server_socket_path).
|
39
|
+
and_return(double('socket', puts: nil, close: nil))
|
40
|
+
server.transmit(message)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe '#transmit_with_response' do
|
45
|
+
let(:message) { { test: 'message' } }
|
46
|
+
|
47
|
+
it 'parses a valid JSON response' do
|
48
|
+
allow(server).to receive(:mkdir_p)
|
49
|
+
mock_socket = double('socket', puts: nil, gets: '{"status": "success"}')
|
50
|
+
expect(UNIXSocket).to receive(:new).and_return(mock_socket)
|
51
|
+
|
52
|
+
response = server.transmit_with_response(message)
|
53
|
+
expect(response).to be_a UnixSocks::Message
|
54
|
+
expect(response.status).to eq 'success'
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'handles JSON parsing errors' do
|
58
|
+
allow(server).to receive(:mkdir_p)
|
59
|
+
mock_socket = double('socket', puts: nil, gets: 'invalid_json')
|
60
|
+
expect(server).to receive(:warn).
|
61
|
+
with(/Caught JSON::ParserError: unexpected character: 'invalid_json'/)
|
62
|
+
expect(UNIXSocket).to receive(:new).and_return(mock_socket)
|
63
|
+
|
64
|
+
response = server.transmit_with_response(message)
|
65
|
+
expect(response).to be nil
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'handles empty responses' do
|
69
|
+
allow(server).to receive(:mkdir_p)
|
70
|
+
mock_socket = double('socket', puts: nil, gets: '')
|
71
|
+
expect(UNIXSocket).to receive(:new).and_return(mock_socket)
|
72
|
+
|
73
|
+
response = server.transmit_with_response(message)
|
74
|
+
expect(response).to be nil
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
describe '#receive' do
|
79
|
+
it 'raises an error if the socket already exists and force is false' do
|
80
|
+
allow(server).to receive(:socket_path_exist?).and_return(true)
|
81
|
+
expect { server.receive(force: false) }.to\
|
82
|
+
raise_error(Errno::EEXIST, /Path already exists/)
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'does not raise an error if force is true' do
|
86
|
+
allow(server).to receive(:socket_path_exist?).and_return(true)
|
87
|
+
expect(Socket).to receive(:unix_server_loop).with(server.server_socket_path)
|
88
|
+
server.receive(force: true)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
describe 'pop_message' do
|
93
|
+
it 'parses a valid JSON message' do
|
94
|
+
mock_socket = double('socket')
|
95
|
+
allow(mock_socket).to receive(:gets).and_return('{"test": "message"}')
|
96
|
+
|
97
|
+
message = server.pop_message(mock_socket)
|
98
|
+
expect(message).to be_a(UnixSocks::Message)
|
99
|
+
expect(message.test).to eq 'message'
|
100
|
+
end
|
101
|
+
|
102
|
+
it 'handles a JSON parsing error' do
|
103
|
+
mock_socket = double('socket')
|
104
|
+
allow(mock_socket).to receive(:gets).and_return('invalid_json')
|
105
|
+
expect(server).to receive(:warn).
|
106
|
+
with(/Caught JSON::ParserError: unexpected character: 'invalid_json'/)
|
107
|
+
expect(server.pop_message(mock_socket)).to be nil
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
describe '#receive_in_background' do
|
112
|
+
it 'runs the receiver in a background thread' do
|
113
|
+
expect(Thread).to receive(:new).and_yield
|
114
|
+
expect(FileUtils).to receive(:rm_f).with(server.server_socket_path)
|
115
|
+
expect(server).to receive(:receive).with(force: true)
|
116
|
+
|
117
|
+
server.receive_in_background(force: true)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
describe '#socket_path_exist?' do
|
122
|
+
it 'returns false if the socket file does not exist' do
|
123
|
+
FileUtils.rm_f(server.server_socket_path)
|
124
|
+
expect(server.socket_path_exist?).to be false
|
125
|
+
end
|
126
|
+
|
127
|
+
it 'returns true if the socket file does exist' do
|
128
|
+
FileUtils.touch(server.server_socket_path)
|
129
|
+
expect(server.socket_path_exist?).to be true
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
describe '#remove_socket_path' do
|
134
|
+
it 'removes the socket file' do
|
135
|
+
expect(FileUtils).to receive(:rm_f).with(server.server_socket_path)
|
136
|
+
|
137
|
+
server.remove_socket_path
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
data/unix_socks.gemspec
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
# stub: unix_socks 0.0.0 ruby lib
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = "unix_socks".freeze
|
6
|
+
s.version = "0.0.0".freeze
|
7
|
+
|
8
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
|
9
|
+
s.require_paths = ["lib".freeze]
|
10
|
+
s.authors = ["Florian Frank".freeze]
|
11
|
+
s.date = "1980-01-02"
|
12
|
+
s.description = "This library enables communication between processes using Unix sockets. It\nhandles message transmission, socket management, and cleanup, supporting\nboth synchronous and asynchronous operations while providing error handling\nfor robust development.\n".freeze
|
13
|
+
s.email = "flori@ping.de".freeze
|
14
|
+
s.extra_rdoc_files = ["README.md".freeze, "lib/unix_socks.rb".freeze, "lib/unix_socks/message.rb".freeze, "lib/unix_socks/server.rb".freeze, "lib/unix_socks/version.rb".freeze]
|
15
|
+
s.files = ["CHANGES.md".freeze, "Gemfile".freeze, "LICENSE".freeze, "README.md".freeze, "Rakefile".freeze, "lib/unix_socks.rb".freeze, "lib/unix_socks/message.rb".freeze, "lib/unix_socks/server.rb".freeze, "lib/unix_socks/version.rb".freeze, "spec/spec_helper.rb".freeze, "spec/unix_socks/message_spec.rb".freeze, "spec/unix_socks/server_spec.rb".freeze, "unix_socks.gemspec".freeze]
|
16
|
+
s.homepage = "https://github.com/flori/unix_socks".freeze
|
17
|
+
s.licenses = ["MIT".freeze]
|
18
|
+
s.rdoc_options = ["--title".freeze, "UnixSocks - A Ruby library for inter-process communication via Unix sockets with\ndynamic message handling\n".freeze, "--main".freeze, "README.md".freeze]
|
19
|
+
s.required_ruby_version = Gem::Requirement.new("~> 3.1".freeze)
|
20
|
+
s.rubygems_version = "3.6.9".freeze
|
21
|
+
s.summary = "A Ruby library for inter-process communication via Unix sockets with dynamic message handling".freeze
|
22
|
+
s.test_files = ["spec/spec_helper.rb".freeze, "spec/unix_socks/message_spec.rb".freeze, "spec/unix_socks/server_spec.rb".freeze]
|
23
|
+
|
24
|
+
s.specification_version = 4
|
25
|
+
|
26
|
+
s.add_development_dependency(%q<gem_hadar>.freeze, ["~> 1.20".freeze])
|
27
|
+
s.add_development_dependency(%q<all_images>.freeze, ["~> 0.4".freeze])
|
28
|
+
s.add_development_dependency(%q<rspec>.freeze, ["~> 3.2".freeze])
|
29
|
+
s.add_development_dependency(%q<debug>.freeze, [">= 0".freeze])
|
30
|
+
s.add_development_dependency(%q<simplecov>.freeze, [">= 0".freeze])
|
31
|
+
s.add_development_dependency(%q<yard>.freeze, [">= 0".freeze])
|
32
|
+
s.add_runtime_dependency(%q<json>.freeze, ["~> 2.0".freeze])
|
33
|
+
s.add_runtime_dependency(%q<tins>.freeze, ["~> 1.3".freeze])
|
34
|
+
end
|
metadata
ADDED
@@ -0,0 +1,183 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: unix_socks
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Florian Frank
|
8
|
+
bindir: bin
|
9
|
+
cert_chain: []
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
11
|
+
dependencies:
|
12
|
+
- !ruby/object:Gem::Dependency
|
13
|
+
name: gem_hadar
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
15
|
+
requirements:
|
16
|
+
- - "~>"
|
17
|
+
- !ruby/object:Gem::Version
|
18
|
+
version: '1.20'
|
19
|
+
type: :development
|
20
|
+
prerelease: false
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
22
|
+
requirements:
|
23
|
+
- - "~>"
|
24
|
+
- !ruby/object:Gem::Version
|
25
|
+
version: '1.20'
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: all_images
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
29
|
+
requirements:
|
30
|
+
- - "~>"
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0.4'
|
33
|
+
type: :development
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - "~>"
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '0.4'
|
40
|
+
- !ruby/object:Gem::Dependency
|
41
|
+
name: rspec
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - "~>"
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '3.2'
|
47
|
+
type: :development
|
48
|
+
prerelease: false
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - "~>"
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '3.2'
|
54
|
+
- !ruby/object:Gem::Dependency
|
55
|
+
name: debug
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '0'
|
61
|
+
type: :development
|
62
|
+
prerelease: false
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - ">="
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '0'
|
68
|
+
- !ruby/object:Gem::Dependency
|
69
|
+
name: simplecov
|
70
|
+
requirement: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - ">="
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '0'
|
75
|
+
type: :development
|
76
|
+
prerelease: false
|
77
|
+
version_requirements: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - ">="
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '0'
|
82
|
+
- !ruby/object:Gem::Dependency
|
83
|
+
name: yard
|
84
|
+
requirement: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - ">="
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '0'
|
89
|
+
type: :development
|
90
|
+
prerelease: false
|
91
|
+
version_requirements: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - ">="
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: '0'
|
96
|
+
- !ruby/object:Gem::Dependency
|
97
|
+
name: json
|
98
|
+
requirement: !ruby/object:Gem::Requirement
|
99
|
+
requirements:
|
100
|
+
- - "~>"
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: '2.0'
|
103
|
+
type: :runtime
|
104
|
+
prerelease: false
|
105
|
+
version_requirements: !ruby/object:Gem::Requirement
|
106
|
+
requirements:
|
107
|
+
- - "~>"
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '2.0'
|
110
|
+
- !ruby/object:Gem::Dependency
|
111
|
+
name: tins
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
113
|
+
requirements:
|
114
|
+
- - "~>"
|
115
|
+
- !ruby/object:Gem::Version
|
116
|
+
version: '1.3'
|
117
|
+
type: :runtime
|
118
|
+
prerelease: false
|
119
|
+
version_requirements: !ruby/object:Gem::Requirement
|
120
|
+
requirements:
|
121
|
+
- - "~>"
|
122
|
+
- !ruby/object:Gem::Version
|
123
|
+
version: '1.3'
|
124
|
+
description: |
|
125
|
+
This library enables communication between processes using Unix sockets. It
|
126
|
+
handles message transmission, socket management, and cleanup, supporting
|
127
|
+
both synchronous and asynchronous operations while providing error handling
|
128
|
+
for robust development.
|
129
|
+
email: flori@ping.de
|
130
|
+
executables: []
|
131
|
+
extensions: []
|
132
|
+
extra_rdoc_files:
|
133
|
+
- README.md
|
134
|
+
- lib/unix_socks.rb
|
135
|
+
- lib/unix_socks/message.rb
|
136
|
+
- lib/unix_socks/server.rb
|
137
|
+
- lib/unix_socks/version.rb
|
138
|
+
files:
|
139
|
+
- CHANGES.md
|
140
|
+
- Gemfile
|
141
|
+
- LICENSE
|
142
|
+
- README.md
|
143
|
+
- Rakefile
|
144
|
+
- lib/unix_socks.rb
|
145
|
+
- lib/unix_socks/message.rb
|
146
|
+
- lib/unix_socks/server.rb
|
147
|
+
- lib/unix_socks/version.rb
|
148
|
+
- spec/spec_helper.rb
|
149
|
+
- spec/unix_socks/message_spec.rb
|
150
|
+
- spec/unix_socks/server_spec.rb
|
151
|
+
- unix_socks.gemspec
|
152
|
+
homepage: https://github.com/flori/unix_socks
|
153
|
+
licenses:
|
154
|
+
- MIT
|
155
|
+
metadata: {}
|
156
|
+
rdoc_options:
|
157
|
+
- "--title"
|
158
|
+
- |
|
159
|
+
UnixSocks - A Ruby library for inter-process communication via Unix sockets with
|
160
|
+
dynamic message handling
|
161
|
+
- "--main"
|
162
|
+
- README.md
|
163
|
+
require_paths:
|
164
|
+
- lib
|
165
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
166
|
+
requirements:
|
167
|
+
- - "~>"
|
168
|
+
- !ruby/object:Gem::Version
|
169
|
+
version: '3.1'
|
170
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
171
|
+
requirements:
|
172
|
+
- - ">="
|
173
|
+
- !ruby/object:Gem::Version
|
174
|
+
version: '0'
|
175
|
+
requirements: []
|
176
|
+
rubygems_version: 3.6.9
|
177
|
+
specification_version: 4
|
178
|
+
summary: A Ruby library for inter-process communication via Unix sockets with dynamic
|
179
|
+
message handling
|
180
|
+
test_files:
|
181
|
+
- spec/spec_helper.rb
|
182
|
+
- spec/unix_socks/message_spec.rb
|
183
|
+
- spec/unix_socks/server_spec.rb
|