nng-ruby 0.1.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.
data/lib/nng/socket.rb ADDED
@@ -0,0 +1,193 @@
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
+ value_ptr = ::FFI::MemoryPointer.new(type)
112
+ ret = case type
113
+ when :bool
114
+ FFI.nng_getopt_bool(@socket, name, value_ptr)
115
+ when :int
116
+ FFI.nng_getopt_int(@socket, name, value_ptr)
117
+ when :size
118
+ FFI.nng_getopt_size(@socket, name, value_ptr)
119
+ when :uint64
120
+ FFI.nng_getopt_uint64(@socket, name, value_ptr)
121
+ when :ms
122
+ FFI.nng_getopt_ms(@socket, name, value_ptr)
123
+ when :string
124
+ FFI.nng_getopt_string(@socket, name, value_ptr)
125
+ else
126
+ raise ArgumentError, "Unknown option type: #{type}"
127
+ end
128
+
129
+ FFI.check_error(ret, "Get option #{name}")
130
+
131
+ if type == :string
132
+ str_ptr = value_ptr.read_pointer
133
+ result = str_ptr.read_string
134
+ FFI.nng_strfree(str_ptr)
135
+ result
136
+ else
137
+ value_ptr.read(type)
138
+ end
139
+ end
140
+
141
+ # Set send timeout
142
+ # @param ms [Integer] timeout in milliseconds
143
+ # @return [self]
144
+ def send_timeout=(ms)
145
+ set_option_ms('send-timeout', ms)
146
+ end
147
+
148
+ # Set receive timeout
149
+ # @param ms [Integer] timeout in milliseconds
150
+ # @return [self]
151
+ def recv_timeout=(ms)
152
+ set_option_ms('recv-timeout', ms)
153
+ end
154
+
155
+ # Set timeout option
156
+ # @param name [String] option name
157
+ # @param ms [Integer] timeout in milliseconds
158
+ # @return [self]
159
+ def set_option_ms(name, ms)
160
+ check_closed
161
+ ret = FFI.nng_setopt_ms(@socket, name, ms)
162
+ FFI.check_error(ret, "Set option #{name}")
163
+ self
164
+ end
165
+
166
+ # Get socket ID
167
+ # @return [Integer] socket ID
168
+ def id
169
+ FFI.nng_socket_id(@socket)
170
+ end
171
+
172
+ # Close the socket
173
+ # @return [nil]
174
+ def close
175
+ return if @closed
176
+ FFI.nng_close(@socket)
177
+ @closed = true
178
+ nil
179
+ end
180
+
181
+ # Check if socket is closed
182
+ # @return [Boolean]
183
+ def closed?
184
+ @closed
185
+ end
186
+
187
+ private
188
+
189
+ def check_closed
190
+ raise Closed, "Socket is closed" if @closed
191
+ end
192
+ end
193
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module NNG
4
+ VERSION = '0.1.0'
5
+ end
data/lib/nng.rb ADDED
@@ -0,0 +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
data/nng.gemspec ADDED
@@ -0,0 +1,65 @@
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 = ['Claude Code']
9
+ spec.email = ['noreply@anthropic.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/yourusername/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/yourusername/nng-ruby'
22
+ spec.metadata['changelog_uri'] = 'https://github.com/yourusername/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
+
45
+ # Extensions (none - we use FFI)
46
+ # spec.extensions = []
47
+
48
+ # Post-install message
49
+ spec.post_install_message = <<~MSG
50
+ ┌───────────────────────────────────────────────────────────┐
51
+ │ Thank you for installing nng-ruby gem! │
52
+ │ │
53
+ │ NNG (nanomsg-next-generation) Ruby bindings │
54
+ │ Version: #{NNG::VERSION} │
55
+ │ │
56
+ │ Quick start: │
57
+ │ require 'nng' │
58
+ │ socket = NNG::Socket.new(:pair1) │
59
+ │ socket.listen("tcp://127.0.0.1:5555") │
60
+ │ │
61
+ │ Documentation: https://rubydoc.info/gems/nng-ruby │
62
+ │ Examples: https://github.com/yourusername/nng-ruby │
63
+ └───────────────────────────────────────────────────────────┘
64
+ MSG
65
+ end
metadata ADDED
@@ -0,0 +1,148 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: nng-ruby
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Claude Code
8
+ bindir: exe
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: ffi
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '1.15'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: '1.15'
26
+ - !ruby/object:Gem::Dependency
27
+ name: bundler
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '2.0'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '2.0'
40
+ - !ruby/object:Gem::Dependency
41
+ name: rake
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '13.0'
47
+ type: :development
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '13.0'
54
+ - !ruby/object:Gem::Dependency
55
+ name: rspec
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '3.0'
61
+ type: :development
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '3.0'
68
+ - !ruby/object:Gem::Dependency
69
+ name: yard
70
+ requirement: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '0.9'
75
+ type: :development
76
+ prerelease: false
77
+ version_requirements: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: '0.9'
82
+ description: Complete Ruby bindings for NNG, a lightweight messaging library. Supports
83
+ all scalability protocols (Pair, Push/Pull, Pub/Sub, Req/Rep, Surveyor/Respondent,
84
+ Bus) and transports (TCP, IPC, Inproc, WebSocket, TLS). Includes bundled libnng
85
+ shared library.
86
+ email:
87
+ - noreply@anthropic.com
88
+ executables: []
89
+ extensions: []
90
+ extra_rdoc_files: []
91
+ files:
92
+ - CHANGELOG.md
93
+ - Gemfile
94
+ - LICENSE
95
+ - README.md
96
+ - Rakefile
97
+ - examples/pair.rb
98
+ - examples/pubsub.rb
99
+ - examples/reqrep.rb
100
+ - lib/nng.rb
101
+ - lib/nng/errors.rb
102
+ - lib/nng/ffi.rb
103
+ - lib/nng/message.rb
104
+ - lib/nng/protocols.rb
105
+ - lib/nng/socket.rb
106
+ - lib/nng/version.rb
107
+ - nng.gemspec
108
+ homepage: https://github.com/yourusername/nng-ruby
109
+ licenses:
110
+ - MIT
111
+ metadata:
112
+ homepage_uri: https://github.com/yourusername/nng-ruby
113
+ source_code_uri: https://github.com/yourusername/nng-ruby
114
+ changelog_uri: https://github.com/yourusername/nng-ruby/blob/main/CHANGELOG.md
115
+ documentation_uri: https://rubydoc.info/gems/nng-ruby
116
+ post_install_message: |
117
+ ┌───────────────────────────────────────────────────────────┐
118
+ │ Thank you for installing nng-ruby gem! │
119
+ │ │
120
+ │ NNG (nanomsg-next-generation) Ruby bindings │
121
+ │ Version: 0.1.0 │
122
+ │ │
123
+ │ Quick start: │
124
+ │ require 'nng' │
125
+ │ socket = NNG::Socket.new(:pair1) │
126
+ │ socket.listen("tcp://127.0.0.1:5555") │
127
+ │ │
128
+ │ Documentation: https://rubydoc.info/gems/nng-ruby │
129
+ │ Examples: https://github.com/yourusername/nng-ruby │
130
+ └───────────────────────────────────────────────────────────┘
131
+ rdoc_options: []
132
+ require_paths:
133
+ - lib
134
+ required_ruby_version: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: 2.5.0
139
+ required_rubygems_version: !ruby/object:Gem::Requirement
140
+ requirements:
141
+ - - ">="
142
+ - !ruby/object:Gem::Version
143
+ version: '0'
144
+ requirements: []
145
+ rubygems_version: 3.6.7
146
+ specification_version: 4
147
+ summary: Ruby bindings for NNG (nanomsg-next-generation)
148
+ test_files: []