nng-ruby 0.1.2 → 1.0.1

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.
data/lib/nng/socket.rb CHANGED
@@ -1,196 +1,196 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'protocols'
4
-
5
- module NNG
6
- # High-level socket interface
7
- class Socket
8
- attr_reader :socket
9
-
10
- # Create a new socket
11
- # @param protocol [Symbol] protocol name (:pair0, :pair1, :push, :pull, :pub, :sub, :req, :rep, :surveyor, :respondent, :bus)
12
- # @param raw [Boolean] open in raw mode
13
- def initialize(protocol, raw: false)
14
- @socket = Protocols.open_socket(protocol, raw: raw)
15
- @closed = false
16
- end
17
-
18
- # Listen on an address
19
- # @param url [String] URL to listen on (e.g., "tcp://0.0.0.0:5555", "ipc:///tmp/test.sock")
20
- # @param flags [Integer] optional flags
21
- # @return [self]
22
- def listen(url, flags: 0)
23
- check_closed
24
- ret = FFI.nng_listen(@socket, url, nil, flags)
25
- FFI.check_error(ret, "Listen on #{url}")
26
- self
27
- end
28
-
29
- # Dial (connect) to an address
30
- # @param url [String] URL to connect to (e.g., "tcp://127.0.0.1:5555")
31
- # @param flags [Integer] optional flags
32
- # @return [self]
33
- def dial(url, flags: 0)
34
- check_closed
35
- ret = FFI.nng_dial(@socket, url, nil, flags)
36
- FFI.check_error(ret, "Dial to #{url}")
37
- self
38
- end
39
-
40
- # Send data
41
- # @param data [String] data to send
42
- # @param flags [Integer] optional flags (e.g., FFI::NNG_FLAG_NONBLOCK)
43
- # @return [self]
44
- def send(data, flags: 0)
45
- check_closed
46
- data_str = data.to_s
47
- data_ptr = ::FFI::MemoryPointer.new(:uint8, data_str.bytesize)
48
- data_ptr.put_bytes(0, data_str)
49
-
50
- ret = FFI.nng_send(@socket, data_ptr, data_str.bytesize, flags)
51
- FFI.check_error(ret, "Send data")
52
- self
53
- end
54
-
55
- # Receive data
56
- # @param flags [Integer] optional flags (e.g., FFI::NNG_FLAG_NONBLOCK)
57
- # @return [String] received data
58
- def recv(flags: FFI::NNG_FLAG_ALLOC)
59
- check_closed
60
-
61
- buf_ptr = ::FFI::MemoryPointer.new(:pointer)
62
- size_ptr = ::FFI::MemoryPointer.new(:size_t)
63
-
64
- ret = FFI.nng_recv(@socket, buf_ptr, size_ptr, flags)
65
- FFI.check_error(ret, "Receive data")
66
-
67
- # Read the data
68
- response_buf = buf_ptr.read_pointer
69
- response_size = size_ptr.read(:size_t)
70
- data = response_buf.read_bytes(response_size)
71
-
72
- # Free NNG-allocated memory
73
- FFI.nng_free(response_buf, response_size)
74
-
75
- data
76
- end
77
-
78
- # Set socket option
79
- # @param name [String] option name
80
- # @param value [Object] option value
81
- # @return [self]
82
- def set_option(name, value)
83
- check_closed
84
-
85
- case value
86
- when true, false
87
- ret = FFI.nng_setopt_bool(@socket, name, value)
88
- when Integer
89
- if value >= 0 && value <= 2**31 - 1
90
- ret = FFI.nng_setopt_int(@socket, name, value)
91
- else
92
- ret = FFI.nng_setopt_uint64(@socket, name, value)
93
- end
94
- when String
95
- ret = FFI.nng_setopt_string(@socket, name, value)
96
- else
97
- raise ArgumentError, "Unsupported option value type: #{value.class}"
98
- end
99
-
100
- FFI.check_error(ret, "Set option #{name}")
101
- self
102
- end
103
-
104
- # Get socket option
105
- # @param name [String] option name
106
- # @param type [Symbol] expected type (:bool, :int, :size, :uint64, :string, :ms)
107
- # @return [Object] option value
108
- def get_option(name, type: :int)
109
- check_closed
110
-
111
- # :ms is actually :int32 (nng_duration)
112
- ptr_type = type == :ms ? :int32 : type
113
- value_ptr = ::FFI::MemoryPointer.new(ptr_type)
114
-
115
- ret = case type
116
- when :bool
117
- FFI.nng_getopt_bool(@socket, name, value_ptr)
118
- when :int
119
- FFI.nng_getopt_int(@socket, name, value_ptr)
120
- when :size
121
- FFI.nng_getopt_size(@socket, name, value_ptr)
122
- when :uint64
123
- FFI.nng_getopt_uint64(@socket, name, value_ptr)
124
- when :ms
125
- FFI.nng_getopt_ms(@socket, name, value_ptr)
126
- when :string
127
- FFI.nng_getopt_string(@socket, name, value_ptr)
128
- else
129
- raise ArgumentError, "Unknown option type: #{type}"
130
- end
131
-
132
- FFI.check_error(ret, "Get option #{name}")
133
-
134
- if type == :string
135
- str_ptr = value_ptr.read_pointer
136
- result = str_ptr.read_string
137
- FFI.nng_strfree(str_ptr)
138
- result
139
- else
140
- value_ptr.read(ptr_type)
141
- end
142
- end
143
-
144
- # Set send timeout
145
- # @param ms [Integer] timeout in milliseconds
146
- # @return [self]
147
- def send_timeout=(ms)
148
- set_option_ms('send-timeout', ms)
149
- end
150
-
151
- # Set receive timeout
152
- # @param ms [Integer] timeout in milliseconds
153
- # @return [self]
154
- def recv_timeout=(ms)
155
- set_option_ms('recv-timeout', ms)
156
- end
157
-
158
- # Set timeout option
159
- # @param name [String] option name
160
- # @param ms [Integer] timeout in milliseconds
161
- # @return [self]
162
- def set_option_ms(name, ms)
163
- check_closed
164
- ret = FFI.nng_setopt_ms(@socket, name, ms)
165
- FFI.check_error(ret, "Set option #{name}")
166
- self
167
- end
168
-
169
- # Get socket ID
170
- # @return [Integer] socket ID
171
- def id
172
- FFI.nng_socket_id(@socket)
173
- end
174
-
175
- # Close the socket
176
- # @return [nil]
177
- def close
178
- return if @closed
179
- FFI.nng_close(@socket)
180
- @closed = true
181
- nil
182
- end
183
-
184
- # Check if socket is closed
185
- # @return [Boolean]
186
- def closed?
187
- @closed
188
- end
189
-
190
- private
191
-
192
- def check_closed
193
- raise Closed, "Socket is closed" if @closed
194
- end
195
- end
196
- end
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'protocols'
4
+
5
+ module NNG
6
+ # High-level socket interface
7
+ class Socket
8
+ attr_reader :socket
9
+
10
+ # Create a new socket
11
+ # @param protocol [Symbol] protocol name (:pair0, :pair1, :push, :pull, :pub, :sub, :req, :rep, :surveyor, :respondent, :bus)
12
+ # @param raw [Boolean] open in raw mode
13
+ def initialize(protocol, raw: false)
14
+ @socket = Protocols.open_socket(protocol, raw: raw)
15
+ @closed = false
16
+ end
17
+
18
+ # Listen on an address
19
+ # @param url [String] URL to listen on (e.g., "tcp://0.0.0.0:5555", "ipc:///tmp/test.sock")
20
+ # @param flags [Integer] optional flags
21
+ # @return [self]
22
+ def listen(url, flags: 0)
23
+ check_closed
24
+ ret = FFI.nng_listen(@socket, url, nil, flags)
25
+ FFI.check_error(ret, "Listen on #{url}")
26
+ self
27
+ end
28
+
29
+ # Dial (connect) to an address
30
+ # @param url [String] URL to connect to (e.g., "tcp://127.0.0.1:5555")
31
+ # @param flags [Integer] optional flags
32
+ # @return [self]
33
+ def dial(url, flags: 0)
34
+ check_closed
35
+ ret = FFI.nng_dial(@socket, url, nil, flags)
36
+ FFI.check_error(ret, "Dial to #{url}")
37
+ self
38
+ end
39
+
40
+ # Send data
41
+ # @param data [String] data to send
42
+ # @param flags [Integer] optional flags (e.g., FFI::NNG_FLAG_NONBLOCK)
43
+ # @return [self]
44
+ def send(data, flags: 0)
45
+ check_closed
46
+ data_str = data.to_s
47
+ data_ptr = ::FFI::MemoryPointer.new(:uint8, data_str.bytesize)
48
+ data_ptr.put_bytes(0, data_str)
49
+
50
+ ret = FFI.nng_send(@socket, data_ptr, data_str.bytesize, flags)
51
+ FFI.check_error(ret, "Send data")
52
+ self
53
+ end
54
+
55
+ # Receive data
56
+ # @param flags [Integer] optional flags (e.g., FFI::NNG_FLAG_NONBLOCK)
57
+ # @return [String] received data
58
+ def recv(flags: FFI::NNG_FLAG_ALLOC)
59
+ check_closed
60
+
61
+ buf_ptr = ::FFI::MemoryPointer.new(:pointer)
62
+ size_ptr = ::FFI::MemoryPointer.new(:size_t)
63
+
64
+ ret = FFI.nng_recv(@socket, buf_ptr, size_ptr, flags)
65
+ FFI.check_error(ret, "Receive data")
66
+
67
+ # Read the data
68
+ response_buf = buf_ptr.read_pointer
69
+ response_size = size_ptr.read(:size_t)
70
+ data = response_buf.read_bytes(response_size)
71
+
72
+ # Free NNG-allocated memory
73
+ FFI.nng_free(response_buf, response_size)
74
+
75
+ data
76
+ end
77
+
78
+ # Set socket option
79
+ # @param name [String] option name
80
+ # @param value [Object] option value
81
+ # @return [self]
82
+ def set_option(name, value)
83
+ check_closed
84
+
85
+ case value
86
+ when true, false
87
+ ret = FFI.nng_setopt_bool(@socket, name, value)
88
+ when Integer
89
+ if value >= 0 && value <= 2**31 - 1
90
+ ret = FFI.nng_setopt_int(@socket, name, value)
91
+ else
92
+ ret = FFI.nng_setopt_uint64(@socket, name, value)
93
+ end
94
+ when String
95
+ ret = FFI.nng_setopt_string(@socket, name, value)
96
+ else
97
+ raise ArgumentError, "Unsupported option value type: #{value.class}"
98
+ end
99
+
100
+ FFI.check_error(ret, "Set option #{name}")
101
+ self
102
+ end
103
+
104
+ # Get socket option
105
+ # @param name [String] option name
106
+ # @param type [Symbol] expected type (:bool, :int, :size, :uint64, :string, :ms)
107
+ # @return [Object] option value
108
+ def get_option(name, type: :int)
109
+ check_closed
110
+
111
+ # :ms is actually :int32 (nng_duration)
112
+ ptr_type = type == :ms ? :int32 : type
113
+ value_ptr = ::FFI::MemoryPointer.new(ptr_type)
114
+
115
+ ret = case type
116
+ when :bool
117
+ FFI.nng_getopt_bool(@socket, name, value_ptr)
118
+ when :int
119
+ FFI.nng_getopt_int(@socket, name, value_ptr)
120
+ when :size
121
+ FFI.nng_getopt_size(@socket, name, value_ptr)
122
+ when :uint64
123
+ FFI.nng_getopt_uint64(@socket, name, value_ptr)
124
+ when :ms
125
+ FFI.nng_getopt_ms(@socket, name, value_ptr)
126
+ when :string
127
+ FFI.nng_getopt_string(@socket, name, value_ptr)
128
+ else
129
+ raise ArgumentError, "Unknown option type: #{type}"
130
+ end
131
+
132
+ FFI.check_error(ret, "Get option #{name}")
133
+
134
+ if type == :string
135
+ str_ptr = value_ptr.read_pointer
136
+ result = str_ptr.read_string
137
+ FFI.nng_strfree(str_ptr)
138
+ result
139
+ else
140
+ value_ptr.read(ptr_type)
141
+ end
142
+ end
143
+
144
+ # Set send timeout
145
+ # @param ms [Integer] timeout in milliseconds
146
+ # @return [self]
147
+ def send_timeout=(ms)
148
+ set_option_ms('send-timeout', ms)
149
+ end
150
+
151
+ # Set receive timeout
152
+ # @param ms [Integer] timeout in milliseconds
153
+ # @return [self]
154
+ def recv_timeout=(ms)
155
+ set_option_ms('recv-timeout', ms)
156
+ end
157
+
158
+ # Set timeout option
159
+ # @param name [String] option name
160
+ # @param ms [Integer] timeout in milliseconds
161
+ # @return [self]
162
+ def set_option_ms(name, ms)
163
+ check_closed
164
+ ret = FFI.nng_setopt_ms(@socket, name, ms)
165
+ FFI.check_error(ret, "Set option #{name}")
166
+ self
167
+ end
168
+
169
+ # Get socket ID
170
+ # @return [Integer] socket ID
171
+ def id
172
+ FFI.nng_socket_id(@socket)
173
+ end
174
+
175
+ # Close the socket
176
+ # @return [nil]
177
+ def close
178
+ return if @closed
179
+ FFI.nng_close(@socket)
180
+ @closed = true
181
+ nil
182
+ end
183
+
184
+ # Check if socket is closed
185
+ # @return [Boolean]
186
+ def closed?
187
+ @closed
188
+ end
189
+
190
+ private
191
+
192
+ def check_closed
193
+ raise Closed, "Socket is closed" if @closed
194
+ end
195
+ end
196
+ end
data/lib/nng/version.rb CHANGED
@@ -1,5 +1,5 @@
1
- # frozen_string_literal: true
2
-
3
- module NNG
4
- VERSION = '0.1.2'
5
- end
1
+ # frozen_string_literal: true
2
+
3
+ module NNG
4
+ VERSION = '1.0.1'
5
+ end
data/lib/nng.rb CHANGED
@@ -1,52 +1,52 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- require_relative 'nng/version'
5
- require_relative 'nng/ffi'
6
- require_relative 'nng/socket'
7
- require_relative 'nng/message'
8
- require_relative 'nng/errors'
9
-
10
- # NNG (nanomsg-next-generation) Ruby bindings
11
- #
12
- # @example Basic usage
13
- # require 'nng'
14
- #
15
- # # Create a pair socket
16
- # socket = NNG::Socket.new(:pair1)
17
- # socket.listen("tcp://127.0.0.1:5555")
18
- #
19
- # # Send a message
20
- # socket.send("Hello, NNG!")
21
- #
22
- # # Receive a message
23
- # data = socket.recv
24
- # puts data
25
- #
26
- # # Close the socket
27
- # socket.close
28
- #
29
- module NNG
30
- class Error < StandardError; end
31
-
32
- # NNG library version
33
- # @return [String] version string
34
- def self.version
35
- VERSION
36
- end
37
-
38
- # NNG library version (from C library)
39
- # @return [String] version string from libnng
40
- def self.lib_version
41
- "#{FFI::NNG_MAJOR_VERSION}.#{FFI::NNG_MINOR_VERSION}.#{FFI::NNG_PATCH_VERSION}"
42
- end
43
-
44
- # Cleanup NNG library (optional, called automatically at exit)
45
- def self.fini
46
- FFI.nng_fini
47
- end
48
-
49
- at_exit do
50
- fini
51
- end
52
- end
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require_relative 'nng/version'
5
+ require_relative 'nng/ffi'
6
+ require_relative 'nng/socket'
7
+ require_relative 'nng/message'
8
+ require_relative 'nng/errors'
9
+
10
+ # NNG (nanomsg-next-generation) Ruby bindings
11
+ #
12
+ # @example Basic usage
13
+ # require 'nng'
14
+ #
15
+ # # Create a pair socket
16
+ # socket = NNG::Socket.new(:pair1)
17
+ # socket.listen("tcp://127.0.0.1:5555")
18
+ #
19
+ # # Send a message
20
+ # socket.send("Hello, NNG!")
21
+ #
22
+ # # Receive a message
23
+ # data = socket.recv
24
+ # puts data
25
+ #
26
+ # # Close the socket
27
+ # socket.close
28
+ #
29
+ module NNG
30
+ class Error < StandardError; end
31
+
32
+ # NNG library version
33
+ # @return [String] version string
34
+ def self.version
35
+ VERSION
36
+ end
37
+
38
+ # NNG library version (from C library)
39
+ # @return [String] version string from libnng runtime
40
+ def self.lib_version
41
+ FFI.nng_version
42
+ end
43
+
44
+ # Cleanup NNG library (optional, called automatically at exit)
45
+ def self.fini
46
+ FFI.nng_fini
47
+ end
48
+
49
+ at_exit do
50
+ fini
51
+ end
52
+ end
data/nng.gemspec CHANGED
@@ -1,67 +1,67 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'lib/nng/version'
4
-
5
- Gem::Specification.new do |spec|
6
- spec.name = 'nng-ruby'
7
- spec.version = NNG::VERSION
8
- spec.authors = ['QingYi']
9
- spec.email = ['qingyi.mail@gmail.com']
10
-
11
- spec.summary = 'Ruby bindings for NNG (nanomsg-next-generation)'
12
- spec.description = 'Complete Ruby bindings for NNG, a lightweight messaging library. ' \
13
- 'Supports all scalability protocols (Pair, Push/Pull, Pub/Sub, Req/Rep, ' \
14
- 'Surveyor/Respondent, Bus) and transports (TCP, IPC, Inproc, WebSocket, TLS). ' \
15
- 'Includes bundled libnng shared library.'
16
- spec.homepage = 'https://github.com/Hola-QingYi/nng-ruby'
17
- spec.license = 'MIT'
18
- spec.required_ruby_version = '>= 2.5.0'
19
-
20
- spec.metadata['homepage_uri'] = spec.homepage
21
- spec.metadata['source_code_uri'] = 'https://github.com/Hola-QingYi/nng-ruby'
22
- spec.metadata['changelog_uri'] = 'https://github.com/Hola-QingYi/nng-ruby/blob/main/CHANGELOG.md'
23
- spec.metadata['documentation_uri'] = 'https://rubydoc.info/gems/nng-ruby'
24
-
25
- # Specify which files should be added to the gem when it is released.
26
- spec.files = Dir.chdir(File.expand_path(__dir__)) do
27
- Dir.glob('{lib,ext}/**/*', File::FNM_DOTMATCH).reject { |f| File.directory?(f) } +
28
- Dir.glob('examples/*.rb') +
29
- %w[README.md LICENSE CHANGELOG.md nng.gemspec Rakefile Gemfile]
30
- end
31
-
32
- spec.bindir = 'exe'
33
- spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
34
- spec.require_paths = ['lib']
35
-
36
- # Runtime dependencies
37
- spec.add_dependency 'ffi', '~> 1.15'
38
-
39
- # Development dependencies
40
- spec.add_development_dependency 'bundler', '~> 2.0'
41
- spec.add_development_dependency 'rake', '~> 13.0'
42
- spec.add_development_dependency 'rspec', '~> 3.0'
43
- spec.add_development_dependency 'yard', '~> 0.9'
44
- spec.add_development_dependency 'rake-compiler', '~> 1.0'
45
-
46
- # Extension configuration (for capturing install-time options)
47
- # We use FFI so no actual compilation, but extconf.rb captures --with-nng-* options
48
- spec.extensions = ['ext/nng/extconf.rb']
49
-
50
- # Post-install message
51
- spec.post_install_message = <<~MSG
52
- ┌───────────────────────────────────────────────────────────┐
53
- │ Thank you for installing nng-ruby gem! │
54
- │ │
55
- │ NNG (nanomsg-next-generation) Ruby bindings │
56
- │ Version: #{NNG::VERSION} │
57
- │ │
58
- │ Quick start: │
59
- │ require 'nng' │
60
- │ socket = NNG::Socket.new(:pair1) │
61
- │ socket.listen("tcp://127.0.0.1:5555") │
62
- │ │
63
- │ Documentation: https://rubydoc.info/gems/nng-ruby │
64
- │ Examples: https://github.com/Hola-QingYi/nng-ruby │
65
- └───────────────────────────────────────────────────────────┘
66
- MSG
67
- end
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lib/nng/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'nng-ruby'
7
+ spec.version = NNG::VERSION
8
+ spec.authors = ['QingYi']
9
+ spec.email = ['qingyi.mail@gmail.com']
10
+
11
+ spec.summary = 'Ruby bindings for NNG (nanomsg-next-generation)'
12
+ spec.description = 'Complete Ruby bindings for NNG, a lightweight messaging library. ' \
13
+ 'Supports all scalability protocols (Pair, Push/Pull, Pub/Sub, Req/Rep, ' \
14
+ 'Surveyor/Respondent, Bus) and transports (TCP, IPC, Inproc, WebSocket, TLS). ' \
15
+ 'Includes bundled libnng shared library.'
16
+ spec.homepage = 'https://github.com/Hola-QingYi/nng-ruby'
17
+ spec.license = 'MIT'
18
+ spec.required_ruby_version = '>= 2.5.0'
19
+
20
+ spec.metadata['homepage_uri'] = spec.homepage
21
+ spec.metadata['source_code_uri'] = 'https://github.com/Hola-QingYi/nng-ruby'
22
+ spec.metadata['changelog_uri'] = 'https://github.com/Hola-QingYi/nng-ruby/blob/main/CHANGELOG.md'
23
+ spec.metadata['documentation_uri'] = 'https://rubydoc.info/gems/nng-ruby'
24
+
25
+ # Specify which files should be added to the gem when it is released.
26
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
27
+ Dir.glob('{lib,ext}/**/*', File::FNM_DOTMATCH).reject { |f| File.directory?(f) } +
28
+ Dir.glob('examples/*.rb') +
29
+ %w[README.md LICENSE CHANGELOG.md nng.gemspec Rakefile Gemfile]
30
+ end
31
+
32
+ spec.bindir = 'exe'
33
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
34
+ spec.require_paths = ['lib']
35
+
36
+ # Runtime dependencies
37
+ spec.add_dependency 'ffi', '~> 1.15'
38
+
39
+ # Development dependencies
40
+ spec.add_development_dependency 'bundler', '~> 2.0'
41
+ spec.add_development_dependency 'rake', '~> 13.0'
42
+ spec.add_development_dependency 'rspec', '~> 3.0'
43
+ spec.add_development_dependency 'yard', '~> 0.9'
44
+ spec.add_development_dependency 'rake-compiler', '~> 1.0'
45
+
46
+ # Extension configuration (for capturing install-time options)
47
+ # We use FFI so no actual compilation, but extconf.rb captures --with-nng-* options
48
+ spec.extensions = ['ext/nng/extconf.rb']
49
+
50
+ # Post-install message
51
+ spec.post_install_message = <<~MSG
52
+ ┌───────────────────────────────────────────────────────────┐
53
+ │ Thank you for installing nng-ruby gem! │
54
+ │ │
55
+ │ NNG (nanomsg-next-generation) Ruby bindings │
56
+ │ Version: #{NNG::VERSION} │
57
+ │ │
58
+ │ Quick start: │
59
+ │ require 'nng' │
60
+ │ socket = NNG::Socket.new(:pair1) │
61
+ │ socket.listen("tcp://127.0.0.1:5555") │
62
+ │ │
63
+ │ Documentation: https://rubydoc.info/gems/nng-ruby │
64
+ │ Examples: https://github.com/Hola-QingYi/nng-ruby │
65
+ └───────────────────────────────────────────────────────────┘
66
+ MSG
67
+ end