takagi 0.1.0 → 1.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.
Files changed (197) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +70 -7
  3. data/.yard/templates/default/layout/html/layout.erb +34 -0
  4. data/AGENTS.md +16 -0
  5. data/CHANGELOG.md +158 -1
  6. data/CODE_OF_CONDUCT.md +1 -1
  7. data/README.md +590 -23
  8. data/ROADMAP.md +55 -0
  9. data/Rakefile +4 -4
  10. data/Steepfile +39 -0
  11. data/bin/takagi-dev +159 -0
  12. data/docs/FIRST_PLUGIN_GUIDE.md +224 -0
  13. data/docs/HOOKS.md +31 -0
  14. data/examples/client_lifecycle_example.rb +118 -0
  15. data/examples/cloud_gateway_app.rb +217 -0
  16. data/examples/nested_api_app.rb +258 -0
  17. data/examples/simple_device_app.rb +71 -0
  18. data/examples/takagi.yml +138 -0
  19. data/lib/takagi/application.rb +256 -0
  20. data/lib/takagi/base/middleware_management.rb +39 -0
  21. data/lib/takagi/base/plugin_management.rb +75 -0
  22. data/lib/takagi/base/reactor_management.rb +104 -0
  23. data/lib/takagi/base/server_lifecycle.rb +156 -0
  24. data/lib/takagi/base.rb +103 -11
  25. data/lib/takagi/branding.rb +88 -0
  26. data/lib/takagi/cbor/decoder.rb +385 -0
  27. data/lib/takagi/cbor/encoder.rb +260 -0
  28. data/lib/takagi/cbor/error.rb +17 -0
  29. data/lib/takagi/cbor/version.rb +9 -0
  30. data/lib/takagi/client/response.rb +236 -0
  31. data/lib/takagi/client.rb +265 -0
  32. data/lib/takagi/client_base.rb +204 -0
  33. data/lib/takagi/coap/code_helpers.rb +190 -0
  34. data/lib/takagi/coap/registries/base.rb +165 -0
  35. data/lib/takagi/coap/registries/content_format.rb +71 -0
  36. data/lib/takagi/coap/registries/message_type.rb +69 -0
  37. data/lib/takagi/coap/registries/method.rb +38 -0
  38. data/lib/takagi/coap/registries/option.rb +71 -0
  39. data/lib/takagi/coap/registries/response.rb +93 -0
  40. data/lib/takagi/coap/registries/signaling.rb +34 -0
  41. data/lib/takagi/coap/signaling.rb +10 -0
  42. data/lib/takagi/coap.rb +37 -0
  43. data/lib/takagi/composite_router.rb +186 -0
  44. data/lib/takagi/config.rb +337 -0
  45. data/lib/takagi/controller/resource_allocator.rb +164 -0
  46. data/lib/takagi/controller/thread_pool.rb +144 -0
  47. data/lib/takagi/controller.rb +319 -0
  48. data/lib/takagi/core/attribute_set.rb +128 -0
  49. data/lib/takagi/discovery/core_link_format.rb +137 -0
  50. data/lib/takagi/errors.rb +536 -0
  51. data/lib/takagi/event_bus/address_prefix.rb +142 -0
  52. data/lib/takagi/event_bus/async_executor.rb +235 -0
  53. data/lib/takagi/event_bus/coap_bridge.rb +208 -0
  54. data/lib/takagi/event_bus/future.rb +153 -0
  55. data/lib/takagi/event_bus/lru_cache.rb +157 -0
  56. data/lib/takagi/event_bus/message_buffer.rb +237 -0
  57. data/lib/takagi/event_bus/observer_cleanup.rb +110 -0
  58. data/lib/takagi/event_bus/scope.rb +74 -0
  59. data/lib/takagi/event_bus.rb +594 -0
  60. data/lib/takagi/helpers.rb +88 -0
  61. data/lib/takagi/hooks.rb +82 -0
  62. data/lib/takagi/initializer.rb +18 -0
  63. data/lib/takagi/logger.rb +15 -6
  64. data/lib/takagi/message/base.rb +155 -0
  65. data/lib/takagi/message/deduplication_cache.rb +84 -0
  66. data/lib/takagi/message/inbound.rb +147 -0
  67. data/lib/takagi/message/outbound.rb +223 -0
  68. data/lib/takagi/message/request.rb +158 -0
  69. data/lib/takagi/message/retransmission_manager.rb +193 -0
  70. data/lib/takagi/middleware/authentication.rb +19 -0
  71. data/lib/takagi/middleware/caching.rb +23 -0
  72. data/lib/takagi/middleware/debugging.rb +16 -0
  73. data/lib/takagi/middleware/logging.rb +14 -0
  74. data/lib/takagi/middleware/metrics.rb +440 -0
  75. data/lib/takagi/middleware/rate_limiting.rb +24 -0
  76. data/lib/takagi/middleware_stack.rb +166 -0
  77. data/lib/takagi/network/base.rb +76 -0
  78. data/lib/takagi/network/framing/tcp.rb +222 -0
  79. data/lib/takagi/network/framing/udp.rb +110 -0
  80. data/lib/takagi/network/registry.rb +72 -0
  81. data/lib/takagi/network/tcp.rb +60 -0
  82. data/lib/takagi/network/tcp_sender.rb +21 -0
  83. data/lib/takagi/network/udp.rb +61 -0
  84. data/lib/takagi/network/udp_sender.rb +20 -0
  85. data/lib/takagi/observable/emitter.rb +62 -0
  86. data/lib/takagi/observable/reactor.rb +488 -0
  87. data/lib/takagi/observable/registry.rb +122 -0
  88. data/lib/takagi/observe_registry.rb +10 -0
  89. data/lib/takagi/observer/client.rb +68 -0
  90. data/lib/takagi/observer/registry.rb +137 -0
  91. data/lib/takagi/observer/sender.rb +39 -0
  92. data/lib/takagi/observer/watcher.rb +43 -0
  93. data/lib/takagi/plugin.rb +313 -0
  94. data/lib/takagi/profiles.rb +176 -0
  95. data/lib/takagi/reactor.rb +23 -0
  96. data/lib/takagi/reactor_registry.rb +64 -0
  97. data/lib/takagi/registry/base.rb +268 -0
  98. data/lib/takagi/response_builder.rb +141 -0
  99. data/lib/takagi/router/metadata_extractor.rb +133 -0
  100. data/lib/takagi/router/route_matcher.rb +83 -0
  101. data/lib/takagi/router.rb +284 -25
  102. data/lib/takagi/serialization/base.rb +102 -0
  103. data/lib/takagi/serialization/cbor_serializer.rb +92 -0
  104. data/lib/takagi/serialization/json_serializer.rb +96 -0
  105. data/lib/takagi/serialization/octet_stream_serializer.rb +82 -0
  106. data/lib/takagi/serialization/registry.rb +187 -0
  107. data/lib/takagi/serialization/text_serializer.rb +87 -0
  108. data/lib/takagi/serialization.rb +117 -0
  109. data/lib/takagi/server/multi.rb +41 -0
  110. data/lib/takagi/server/registry.rb +71 -0
  111. data/lib/takagi/server/tcp.rb +249 -0
  112. data/lib/takagi/server/udp.rb +139 -0
  113. data/lib/takagi/server/udp_worker.rb +174 -0
  114. data/lib/takagi/server.rb +1 -31
  115. data/lib/takagi/server_registry.rb +10 -0
  116. data/lib/takagi/tcp_client.rb +142 -0
  117. data/lib/takagi/version.rb +2 -1
  118. data/lib/takagi.rb +24 -3
  119. data/sig/takagi/application.rbs +48 -0
  120. data/sig/takagi/base/middleware_management.rbs +33 -0
  121. data/sig/takagi/base/reactor_management.rbs +52 -0
  122. data/sig/takagi/base/server_lifecycle.rbs +54 -0
  123. data/sig/takagi/base.rbs +48 -0
  124. data/sig/takagi/cbor/decoder.rbs +171 -0
  125. data/sig/takagi/cbor/encoder.rbs +146 -0
  126. data/sig/takagi/cbor/error.rbs +19 -0
  127. data/sig/takagi/cbor/version.rbs +7 -0
  128. data/sig/takagi/client/response.rbs +148 -0
  129. data/sig/takagi/client.rbs +119 -0
  130. data/sig/takagi/client_base.rbs +135 -0
  131. data/sig/takagi/coap/code_helpers.rbs +91 -0
  132. data/sig/takagi/coap/registries/base.rbs +95 -0
  133. data/sig/takagi/coap/registries/content_format.rbs +47 -0
  134. data/sig/takagi/coap/registries/message_type.rbs +53 -0
  135. data/sig/takagi/coap/registries/method.rbs +27 -0
  136. data/sig/takagi/coap/registries/option.rbs +43 -0
  137. data/sig/takagi/coap/registries/response.rbs +52 -0
  138. data/sig/takagi/coap.rbs +24 -0
  139. data/sig/takagi/composite_router.rbs +46 -0
  140. data/sig/takagi/config.rbs +134 -0
  141. data/sig/takagi/controller.rbs +73 -0
  142. data/sig/takagi/core/attribute_set.rbs +57 -0
  143. data/sig/takagi/discovery/core_link_format.rbs +50 -0
  144. data/sig/takagi/event_bus/address_prefix.rbs +78 -0
  145. data/sig/takagi/event_bus/async_executor.rbs +88 -0
  146. data/sig/takagi/event_bus/coap_bridge.rbs +93 -0
  147. data/sig/takagi/event_bus/future.rbs +78 -0
  148. data/sig/takagi/event_bus/lru_cache.rbs +86 -0
  149. data/sig/takagi/event_bus/message_buffer.rbs +133 -0
  150. data/sig/takagi/event_bus/observer_cleanup.rbs +62 -0
  151. data/sig/takagi/event_bus.rbs +320 -0
  152. data/sig/takagi/helpers.rbs +34 -0
  153. data/sig/takagi/initializer.rbs +9 -0
  154. data/sig/takagi/logger.rbs +17 -0
  155. data/sig/takagi/message/base.rbs +64 -0
  156. data/sig/takagi/message/deduplication_cache.rbs +49 -0
  157. data/sig/takagi/message/inbound.rbs +76 -0
  158. data/sig/takagi/message/outbound.rbs +48 -0
  159. data/sig/takagi/message/request.rbs +32 -0
  160. data/sig/takagi/message/retransmission_manager.rbs +76 -0
  161. data/sig/takagi/middleware/authentication.rbs +11 -0
  162. data/sig/takagi/middleware/caching.rbs +13 -0
  163. data/sig/takagi/middleware/debugging.rbs +9 -0
  164. data/sig/takagi/middleware/logging.rbs +7 -0
  165. data/sig/takagi/middleware/metrics.rbs +15 -0
  166. data/sig/takagi/middleware/rate_limiting.rbs +13 -0
  167. data/sig/takagi/middleware_stack.rbs +69 -0
  168. data/sig/takagi/network/tcp_sender.rbs +10 -0
  169. data/sig/takagi/network/udp_sender.rbs +14 -0
  170. data/sig/takagi/observe_registry.rbs +36 -0
  171. data/sig/takagi/observer/client.rbs +36 -0
  172. data/sig/takagi/observer/sender.rbs +12 -0
  173. data/sig/takagi/observer/watcher.rbs +18 -0
  174. data/sig/takagi/profiles.rbs +33 -0
  175. data/sig/takagi/reactor.rbs +20 -0
  176. data/sig/takagi/reactor_registry.rbs +14 -0
  177. data/sig/takagi/response_builder.rbs +12 -0
  178. data/sig/takagi/router/metadata_extractor.rbs +71 -0
  179. data/sig/takagi/router/route_matcher.rbs +43 -0
  180. data/sig/takagi/router.rbs +166 -0
  181. data/sig/takagi/serialization.rbs +32 -0
  182. data/sig/takagi/server/multi.rbs +16 -0
  183. data/sig/takagi/server/tcp.rbs +42 -0
  184. data/sig/takagi/server/udp.rbs +52 -0
  185. data/sig/takagi/server/udp_worker.rbs +42 -0
  186. data/sig/takagi/server.rbs +4 -0
  187. data/sig/takagi/server_registry.rbs +71 -0
  188. data/sig/takagi/tcp_client.rbs +23 -0
  189. data/sig/takagi/version.rbs +5 -0
  190. data/takagi.gemspec +37 -35
  191. metadata +204 -31
  192. data/.idea/.gitignore +0 -8
  193. data/.idea/misc.xml +0 -4
  194. data/.idea/modules.xml +0 -8
  195. data/.idea/takagi.iml +0 -81
  196. data/.idea/vcs.xml +0 -6
  197. data/lib/takagi/message.rb +0 -75
@@ -0,0 +1,166 @@
1
+ module Takagi
2
+ class Router
3
+ @routes: untyped
4
+
5
+ @routes_mutex: untyped
6
+
7
+ @logger: untyped
8
+
9
+ @route_matcher: untyped
10
+
11
+ @metadata_extractor: untyped
12
+
13
+ DEFAULT_CONTENT_FORMAT: 50
14
+
15
+ # Global singleton instance for backward compatibility
16
+ def self.instance: () -> Router
17
+
18
+ # Reset the global instance (for testing)
19
+ def self.reset!: () -> void
20
+
21
+ # Represents a registered route with its handler and CoRE Link Format metadata
22
+ class RouteEntry
23
+ @method: untyped
24
+
25
+ @path: untyped
26
+
27
+ @block: untyped
28
+
29
+ @receiver: untyped
30
+
31
+ @attribute_set: untyped
32
+
33
+ attr_reader method: untyped
34
+
35
+ attr_reader path: untyped
36
+
37
+ attr_reader block: untyped
38
+
39
+ attr_reader receiver: untyped
40
+
41
+ attr_reader attribute_set: untyped
42
+
43
+ def initialize: (method: untyped, path: untyped, block: untyped, ?metadata: ::Hash[untyped, untyped], ?receiver: untyped?) -> void
44
+
45
+ # Returns the underlying metadata hash for backward compatibility
46
+ def metadata: () -> untyped
47
+
48
+ # Configure CoRE Link Format attributes using DSL block
49
+ #
50
+ # @example
51
+ # entry.configure_attributes do
52
+ # rt 'sensor'
53
+ # obs true
54
+ # ct 'application/json'
55
+ # end
56
+ def configure_attributes: () { (?) -> untyped } -> untyped
57
+
58
+ # Support for dup operation (used in discovery)
59
+ def initialize_copy: (untyped original) -> untyped
60
+ end
61
+
62
+ # Provides the execution context for route handlers, exposing helper
63
+ # methods for configuring CoRE Link Format attributes via a small DSL.
64
+ class RouteContext
65
+ @entry: untyped
66
+
67
+ @request: untyped
68
+
69
+ @params: untyped
70
+
71
+ @receiver: untyped
72
+
73
+ # Create a fresh AttributeSet for this request to avoid cross-request state sharing
74
+ # Initialize it with a copy of the entry's current metadata
75
+ @core_attributes: untyped
76
+
77
+ extend Forwardable
78
+
79
+ include Takagi::Helpers
80
+
81
+ attr_reader request: untyped
82
+
83
+ attr_reader params: untyped
84
+
85
+ # Aliases for common methods
86
+ alias content_format ct
87
+
88
+ alias observable obs
89
+
90
+ alias if_ interface
91
+
92
+ def initialize: (untyped entry, untyped request, untyped params, untyped receiver) -> void
93
+
94
+ def run: (untyped block) -> untyped
95
+
96
+ private
97
+
98
+ # Delegates method calls to the receiver (application instance)
99
+ # This allows route handlers to call application methods within their blocks
100
+ # Example: get '/users' do; fetch_users; end - calls application's fetch_users method
101
+ def method_missing: (untyped name, *untyped) ?{ (?) -> untyped } -> untyped
102
+
103
+ # Required pair for method_missing to properly support respond_to?
104
+ def respond_to_missing?: (untyped name, ?bool include_private) -> untyped
105
+ end
106
+
107
+ def initialize: () -> void
108
+
109
+ # Registers a new route for a given HTTP method and path
110
+ # @param method [String] The HTTP method (GET, POST, etc.)
111
+ # @param path [String] The URL path, can include dynamic segments like `:id`
112
+ # @param block [Proc] The handler to be executed when the route is matched
113
+ def add_route: (untyped method, untyped path, ?metadata: ::Hash[untyped, untyped]) { (?) -> untyped } -> untyped
114
+
115
+ # Registers a OBSERVE route
116
+ # @param path [String] The URL path
117
+ # @param block [Proc] The handler function
118
+ def observable: (untyped path, ?metadata: ::Hash[untyped, untyped]) { (?) -> untyped } -> untyped
119
+
120
+ def all_routes: () -> untyped
121
+
122
+ def find_observable: (untyped path) -> untyped
123
+
124
+ # Finds a registered route for a given method and path
125
+ # @param method [String] HTTP method
126
+ # @param path [String] URL path
127
+ # @return [Proc, Hash] The matching handler and extracted parameters
128
+ def find_route: (untyped method, untyped path) -> untyped
129
+
130
+ def link_format_entries: () -> untyped
131
+
132
+ # Applies CoRE metadata outside the request cycle. Useful for boot time
133
+ # configuration where the DSL block does not have a live request object.
134
+ def configure_core: (untyped method, untyped path) ?{ (?) -> untyped } -> (nil | untyped)
135
+
136
+ private
137
+
138
+ def wrap_block: (untyped entry) -> (nil | untyped)
139
+
140
+ # Matches dynamic routes that contain parameters (e.g., `/users/:id`)
141
+ # Delegates to RouteMatcher for the actual matching logic
142
+ # @param method [String] HTTP method
143
+ # @param path [String] Request path
144
+ # @return [Array(RouteEntry, Hash)] Matched route entry and extracted parameters
145
+ def match_dynamic_route: (untyped method, untyped path) -> untyped
146
+
147
+ def build_route_entry: (untyped method, untyped path, untyped metadata, untyped block) -> untyped
148
+
149
+ # Normalizes route metadata with sensible defaults for CoRE Link Format
150
+ #
151
+ # @param method [String] HTTP-like method (GET, POST, OBSERVE, etc.)
152
+ # @param path [String] Route path
153
+ # @param metadata [Hash, nil] User-provided metadata
154
+ # @return [Hash] Normalized metadata with defaults applied
155
+ def normalize_metadata: (untyped method, untyped path, untyped metadata) -> untyped
156
+
157
+ def default_resource_type: (untyped method) -> ("core#observable" | "core#endpoint")
158
+
159
+ def default_interface: (untyped method) -> ("takagi.observe" | ::String)
160
+
161
+ # Executes route handler in metadata extraction mode to capture core block attributes
162
+ # Delegates to MetadataExtractor for the actual extraction logic
163
+ # @param entry [RouteEntry] The route entry to extract metadata from
164
+ def extract_metadata_from_handler: (untyped entry) -> untyped
165
+ end
166
+ end
@@ -0,0 +1,32 @@
1
+ # Takagi::Serialization - Content-format serialization system
2
+ module Takagi
3
+ module Serialization
4
+ # Custom error classes
5
+ class Error < StandardError
6
+ end
7
+
8
+ class UnknownFormatError < Error
9
+ end
10
+
11
+ class EncodeError < Error
12
+ end
13
+
14
+ class DecodeError < Error
15
+ end
16
+
17
+ class InvalidSerializerError < Error
18
+ end
19
+
20
+ # Register default serializers
21
+ def self.register_defaults!: () -> void
22
+
23
+ # Encode data with format
24
+ def self.encode: (untyped data, Integer format) -> String
25
+
26
+ # Decode bytes with format
27
+ def self.decode: (String bytes, Integer format) -> untyped
28
+
29
+ # Check if format is supported
30
+ def self.supports?: (Integer format) -> bool
31
+ end
32
+ end
@@ -0,0 +1,16 @@
1
+ module Takagi
2
+ module Server
3
+ # Helper class to run multiple servers concurrently
4
+ class Multi
5
+ @servers: untyped
6
+
7
+ @threads: untyped
8
+
9
+ def initialize: (untyped servers) -> void
10
+
11
+ def run!: () -> untyped
12
+
13
+ def shutdown!: () -> untyped
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,42 @@
1
+ module Takagi
2
+ module Server
3
+ # TCP server implementation for CoAP over TCP
4
+ class Tcp
5
+ @port: untyped
6
+
7
+ @worker_threads: untyped
8
+
9
+ @middleware_stack: untyped
10
+
11
+ @router: untyped
12
+
13
+ @logger: untyped
14
+
15
+ @watcher: untyped
16
+
17
+ @server: untyped
18
+
19
+ @sender: untyped
20
+
21
+ @workers: untyped
22
+
23
+ @shutdown_called: untyped
24
+
25
+ def initialize: (?port: ::Integer, ?worker_threads: ::Integer, ?middleware_stack: untyped?, ?router: untyped?, ?logger: untyped?, ?watcher: untyped?, ?sender: untyped?) -> void
26
+
27
+ def run!: () -> untyped
28
+
29
+ def shutdown!: () -> (nil | untyped)
30
+
31
+ private
32
+
33
+ def handle_connection: (untyped sock) -> untyped
34
+
35
+ def read_request: (untyped sock) -> (nil | untyped)
36
+
37
+ def build_response: (untyped inbound_request) -> untyped
38
+
39
+ def transmit_response: (untyped sock, untyped response) -> untyped
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,52 @@
1
+ module Takagi
2
+ module Server
3
+ # UDP server for handling CoAP messages
4
+ class Udp
5
+ @port: untyped
6
+
7
+ @worker_processes: untyped
8
+
9
+ @worker_threads: untyped
10
+
11
+ @middleware_stack: untyped
12
+
13
+ @router: untyped
14
+
15
+ @logger: untyped
16
+
17
+ @watcher: untyped
18
+
19
+ @socket: untyped
20
+
21
+ @sender: untyped
22
+
23
+ @shutdown_called: untyped
24
+
25
+ @worker_pids: untyped
26
+
27
+ def initialize: (?port: ::Integer, ?worker_processes: ::Integer, ?worker_threads: ::Integer, ?middleware_stack: untyped?, ?router: untyped?, ?logger: untyped?, ?watcher: untyped?) -> void
28
+
29
+ # Starts the server with multiple worker processes
30
+ def run!: () -> untyped
31
+
32
+ # Gracefully shuts down all workers
33
+ def shutdown!: () -> (nil | untyped)
34
+
35
+ private
36
+
37
+ def log_boot_details: () -> untyped
38
+
39
+ def spawn_workers: () -> untyped
40
+
41
+ def fork_worker: () -> untyped
42
+
43
+ def worker_configuration: () -> { port: untyped, socket: untyped, middleware_stack: untyped, sender: untyped, logger: untyped, threads: untyped }
44
+
45
+ def close_socket: () -> untyped
46
+
47
+ def terminate_workers: () -> (nil | untyped)
48
+
49
+ def test_environment?: () -> untyped
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,42 @@
1
+ module Takagi
2
+ module Server
3
+ # Handles incoming UDP messages on behalf of the master Udp server.
4
+ class UdpWorker
5
+ @socket: untyped
6
+
7
+ @middleware_stack: untyped
8
+
9
+ @sender: untyped
10
+
11
+ @logger: untyped
12
+
13
+ @port: untyped
14
+
15
+ @threads: untyped
16
+
17
+ @dedup_cache: untyped
18
+
19
+ def initialize: (socket: untyped, middleware_stack: untyped, **untyped options) -> void
20
+
21
+ def run: () -> untyped
22
+
23
+ private
24
+
25
+ def process_loop: (untyped queue) -> untyped
26
+
27
+ def spawn_thread: (untyped queue) -> untyped
28
+
29
+ def handle_request: (untyped request, untyped addr) -> untyped
30
+
31
+ def log_inbound_request: (untyped inbound_request) -> untyped
32
+
33
+ def immediate_response: (untyped inbound_request) -> (untyped | nil)
34
+
35
+ def log_middleware_result: (untyped result) -> (nil | untyped)
36
+
37
+ def build_response: (untyped inbound_request, untyped result) -> untyped
38
+
39
+ def transmit: (untyped response, untyped addr) -> untyped
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,4 @@
1
+ module Takagi
2
+ module Server
3
+ end
4
+ end
@@ -0,0 +1,71 @@
1
+ module Takagi
2
+ # Registry for CoAP server implementations
3
+ #
4
+ # Allows registering different protocol implementations (UDP, TCP, DTLS, QUIC, etc.)
5
+ # without modifying core code. Follows the Open/Closed Principle.
6
+ #
7
+ # @example Registering a server
8
+ # ServerRegistry.register(:udp, Server::Udp)
9
+ # ServerRegistry.register(:tcp, Server::Tcp)
10
+ #
11
+ # @example Building a server
12
+ # server = ServerRegistry.build(:tcp, port: 5683, worker_threads: 4)
13
+ #
14
+ # @example Adding a custom protocol
15
+ # class MyCustomServer
16
+ # def initialize(port:, **options)
17
+ # # ...
18
+ # end
19
+ # end
20
+ # ServerRegistry.register(:custom, MyCustomServer)
21
+ class ServerRegistry
22
+ self.@servers: untyped
23
+
24
+ self.@mutex: untyped
25
+
26
+ class ProtocolNotFoundError < StandardError
27
+ end
28
+
29
+ # Register a server implementation for a protocol
30
+ #
31
+ # @param protocol [Symbol] Protocol identifier (:udp, :tcp, :dtls, etc.)
32
+ # @param klass [Class] Server class that responds to .new
33
+ # @param options [Hash] Optional metadata (description, rfc, etc.)
34
+ #
35
+ # @example
36
+ # ServerRegistry.register(:udp, Server::Udp, rfc: 'RFC 7252')
37
+ # ServerRegistry.register(:tcp, Server::Tcp, rfc: 'RFC 8323')
38
+ def self.register: (untyped protocol, untyped klass, **untyped options) -> untyped
39
+
40
+ # Build a server instance for the given protocol
41
+ #
42
+ # @param protocol [Symbol] Protocol identifier
43
+ # @param options [Hash] Options to pass to server constructor
44
+ # @return [Object] Server instance
45
+ # @raise [ProtocolNotFoundError] If protocol is not registered
46
+ #
47
+ # @example
48
+ # server = ServerRegistry.build(:tcp, port: 5683, worker_threads: 4)
49
+ def self.build: (untyped protocol, **untyped options) -> untyped
50
+
51
+ # Check if a protocol is registered
52
+ #
53
+ # @param protocol [Symbol] Protocol identifier
54
+ # @return [Boolean] true if registered
55
+ def self.registered?: (untyped protocol) -> untyped
56
+
57
+ # Get all registered protocols
58
+ #
59
+ # @return [Array<Symbol>] List of protocol identifiers
60
+ def self.protocols: () -> untyped
61
+
62
+ # Get metadata for a protocol
63
+ #
64
+ # @param protocol [Symbol] Protocol identifier
65
+ # @return [Hash, nil] Metadata hash or nil if not found
66
+ def self.metadata_for: (untyped protocol) -> untyped
67
+
68
+ # Clear all registrations (useful for testing)
69
+ def self.clear!: () -> untyped
70
+ end
71
+ end
@@ -0,0 +1,23 @@
1
+ module Takagi
2
+ # CoAP-over-TCP client for testing Takagi servers with TCP transport.
3
+ #
4
+ # This client implements CoAP over TCP (RFC 8323) with automatic length framing.
5
+ # Unlike the UDP client, TCP provides reliable delivery so no retransmission
6
+ # manager is needed.
7
+ #
8
+ # @example Basic usage with auto-close (recommended)
9
+ # Takagi::TcpClient.open('coap+tcp://localhost:5683') do |client|
10
+ # client.get('/temperature')
11
+ # end
12
+ #
13
+ # @example Manual lifecycle management
14
+ # client = Takagi::TcpClient.new('coap+tcp://localhost:5683')
15
+ # begin
16
+ # client.get('/temperature')
17
+ # ensure
18
+ # client.close
19
+ # end
20
+ class TcpClient < ClientBase
21
+ def request: (untyped method, untyped path, ?untyped? payload) { (?) -> untyped } -> untyped
22
+ end
23
+ end
@@ -0,0 +1,5 @@
1
+ module Takagi
2
+ VERSION: "1.0.0"
3
+
4
+ NAME: "It works on my machine"
5
+ end
data/takagi.gemspec CHANGED
@@ -1,44 +1,46 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "lib/takagi/version"
3
+ require_relative 'lib/takagi/version'
4
+
5
+ # Compute files at the top level so the Gem::Specification block can
6
+ # reference it (the block runs in instance context, not top-level, so
7
+ # a top-level `def gem_files` is not visible inside `spec.files =`).
8
+ FILES = Dir.chdir(__dir__) do
9
+ `git ls-files -z`.split("\x0").reject do |file|
10
+ (File.expand_path(file) == __FILE__) ||
11
+ file.start_with?(*%w[bin/ test/ spec/ features/ .git .github appveyor Gemfile])
12
+ end
13
+ end
14
+
15
+ HOMEPAGE = 'https://github.com/takagi-works/takagi'
4
16
 
5
17
  Gem::Specification.new do |spec|
6
- spec.name = "takagi"
18
+ spec.name = 'takagi'
7
19
  spec.version = Takagi::VERSION
8
- spec.authors = ["Dominik Matoulek"]
9
- spec.email = ["domitea@gmail.com"]
10
-
11
- spec.summary = "Lightweight CoAP framework for Ruby"
12
- spec.description = "Sinatra-like framework for CoAP and IoT messaging."
13
- spec.homepage = "https://github.com/domitea/takagi"
14
- spec.license = "MIT"
15
- spec.required_ruby_version = ">= 3.0.0"
16
-
17
- spec.metadata["allowed_push_host"] ||= "https://rubygems.org"
18
-
19
- spec.metadata["homepage_uri"] = spec.homepage
20
- spec.metadata["source_code_uri"] = "https://github.com/domitea/takagi"
21
- spec.metadata["changelog_uri"] = "https://github.com/domitea/takagi/CHANGELOG.md"
22
-
23
- # Specify which files should be added to the gem when it is released.
24
- # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
25
- spec.files = Dir.chdir(__dir__) do
26
- `git ls-files -z`.split("\x0").reject do |f|
27
- (File.expand_path(f) == __FILE__) ||
28
- f.start_with?(*%w[bin/ test/ spec/ features/ .git .github appveyor Gemfile])
29
- end
30
- end
31
- spec.bindir = "exe"
32
- spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
33
- spec.require_paths = ["lib"]
20
+ spec.authors = ['Dominik Matoulek']
21
+ spec.email = ['domitea@gmail.com']
22
+
23
+ spec.summary = 'Lightweight CoAP framework for Ruby'
24
+ spec.description = 'Sinatra-like framework for CoAP and IoT messaging.'
25
+ spec.homepage = HOMEPAGE
26
+ spec.license = 'MIT'
27
+ spec.required_ruby_version = '>= 3.0.0'
28
+
29
+ spec.metadata['allowed_push_host'] ||= 'https://rubygems.org'
30
+
31
+ spec.metadata['homepage_uri'] = HOMEPAGE
32
+ spec.metadata['source_code_uri'] = "#{HOMEPAGE}.git"
33
+ spec.metadata['changelog_uri'] = "#{HOMEPAGE}/blob/master/CHANGELOG.md"
34
+
35
+ spec.files = FILES
36
+ spec.bindir = 'bin'
37
+ spec.executables = ['takagi-dev']
38
+ spec.require_paths = ['lib']
34
39
 
35
- # Uncomment to register a new dependency of your gem
36
- # spec.add_dependency "example-gem", "~> 1.0"
37
- spec.add_dependency "rack"
38
- spec.add_dependency "sequel"
40
+ spec.add_dependency 'rack', '~> 2.0'
41
+ spec.add_dependency 'zeitwerk', '~> 2.0'
39
42
 
40
- spec.add_development_dependency "rspec"
43
+ spec.add_development_dependency 'rspec', '~> 3.0'
41
44
 
42
- # For more information and examples about making a new gem, check out our
43
- # guide at: https://bundler.io/guides/creating_gem.html
45
+ spec.metadata['rubygems_mfa_required'] = 'true'
44
46
  end