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,165 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Takagi
4
+ module CoAP
5
+ module Registries
6
+ # Base class for extensible constant registries.
7
+ #
8
+ # Provides a pattern for registering CoAP protocol constants with:
9
+ # - Numeric value
10
+ # - Human-readable name
11
+ # - Symbol accessor (optional)
12
+ # - Thread-safe storage
13
+ #
14
+ # This allows plugins to extend the protocol without modifying core code.
15
+ #
16
+ # @example Creating a custom registry
17
+ # class MyRegistry < Registries::Base
18
+ # register(1, 'First Value', :first)
19
+ # register(2, 'Second Value', :second)
20
+ # end
21
+ #
22
+ # MyRegistry::FIRST # => 1
23
+ # MyRegistry.name_for(1) # => "First Value"
24
+ # MyRegistry.all # => {1 => "First Value", 2 => "Second Value"}
25
+ require 'takagi/hooks'
26
+
27
+ class Base
28
+ def self.inherited(subclass)
29
+ super
30
+ subclass.instance_variable_set(:@mutex, Mutex.new)
31
+ end
32
+
33
+ class << self
34
+ # Register a new constant in the registry
35
+ #
36
+ # @param value [Integer] Numeric value of the constant
37
+ # @param name [String] Human-readable name
38
+ # @param symbol [Symbol, nil] Optional symbol for constant access
39
+ # @param rfc [String, nil] Optional RFC reference
40
+ #
41
+ # @example
42
+ # register(69, '2.05 Content', :content, rfc: 'RFC 7252 §5.9.1.4')
43
+ def register(value, name, symbol = nil, rfc: nil)
44
+ @mutex.synchronize do
45
+ registry[value] = {
46
+ name: name,
47
+ symbol: symbol,
48
+ rfc: rfc
49
+ }
50
+
51
+ # Create constant if symbol provided
52
+ if symbol
53
+ const_name = symbol.to_s.upcase
54
+ const_set(const_name, value) unless const_defined?(const_name, false)
55
+ end
56
+
57
+ # Store reverse lookup
58
+ reverse_registry[name] = value if name
59
+ reverse_registry[symbol] = value if symbol
60
+ end
61
+
62
+ Takagi::Hooks.emit(
63
+ :coap_registry_registered,
64
+ registry: self,
65
+ value: value,
66
+ name: name,
67
+ symbol: symbol,
68
+ rfc: rfc
69
+ )
70
+ end
71
+
72
+ # Get human-readable name for a value
73
+ #
74
+ # @param value [Integer] The numeric value
75
+ # @return [String, nil] The name, or nil if not found
76
+ def name_for(value)
77
+ @mutex.synchronize { registry[value]&.dig(:name) }
78
+ end
79
+
80
+ # Get numeric value for a name or symbol
81
+ #
82
+ # @param key [String, Symbol] The name or symbol
83
+ # @return [Integer, nil] The value, or nil if not found
84
+ def value_for(key)
85
+ @mutex.synchronize { reverse_registry[key] }
86
+ end
87
+
88
+ # Get RFC reference for a value
89
+ #
90
+ # @param value [Integer] The numeric value
91
+ # @return [String, nil] The RFC reference, or nil if not found
92
+ def rfc_for(value)
93
+ @mutex.synchronize { registry[value]&.dig(:rfc) }
94
+ end
95
+
96
+ # Check if a value is registered
97
+ #
98
+ # @param value [Integer] The value to check
99
+ # @return [Boolean] true if registered
100
+ def registered?(value)
101
+ @mutex.synchronize { registry.key?(value) }
102
+ end
103
+
104
+ # Get all registered constants
105
+ #
106
+ # @return [Hash] Map of value => name
107
+ def all
108
+ @mutex.synchronize { registry.transform_values { |info| info[:name] } }
109
+ end
110
+
111
+ # Iterate over registered values
112
+ #
113
+ # @yield [Integer] each registered numeric value
114
+ # @return [Enumerator] if no block given
115
+ def each_value(&block)
116
+ return enum_for(:each_value) unless block_given?
117
+
118
+ # Get snapshot to avoid holding lock during iteration
119
+ snapshot = @mutex.synchronize { registry.keys.dup }
120
+ snapshot.each(&block)
121
+ end
122
+
123
+ # Get all registered values
124
+ #
125
+ # @return [Array<Integer>] Array of all values
126
+ def values
127
+ @mutex.synchronize { registry.keys.dup }
128
+ end
129
+
130
+ # Get registry metadata for a value
131
+ #
132
+ # @param value [Integer] The numeric value
133
+ # @return [Hash, nil] Full metadata hash
134
+ def metadata_for(value)
135
+ @mutex.synchronize { registry[value]&.dup }
136
+ end
137
+
138
+ # Clear all registrations (useful for testing)
139
+ def clear!
140
+ @mutex.synchronize do
141
+ registry.clear
142
+ reverse_registry.clear
143
+ # Remove constants (carefully)
144
+ constants(false).each do |const_name|
145
+ remove_const(const_name) if const_name =~ /^[A-Z_]+$/
146
+ end
147
+ end
148
+
149
+ Takagi::Hooks.emit(:coap_registry_cleared, registry: self)
150
+ end
151
+
152
+ private
153
+
154
+ def registry
155
+ @registry ||= {}
156
+ end
157
+
158
+ def reverse_registry
159
+ @reverse_registry ||= {}
160
+ end
161
+ end
162
+ end
163
+ end
164
+ end
165
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Takagi
4
+ module CoAP
5
+ module Registries
6
+ # CoAP Content-Format Registry (RFC 7252 §12.3)
7
+ #
8
+ # Extensible registry for CoAP content-format codes.
9
+ # Plugins can register custom content formats without modifying core code.
10
+ #
11
+ # @example Using predefined formats
12
+ # Takagi::CoAP::Registries::ContentFormat::JSON # => 50
13
+ # Takagi::CoAP::Registries::ContentFormat::TEXT_PLAIN # => 0
14
+ #
15
+ # @example Registering a custom format
16
+ # Takagi::CoAP::Registries::ContentFormat.register(65000, 'application/custom', :custom)
17
+ # Takagi::CoAP::Registries::ContentFormat::CUSTOM # => 65000
18
+ #
19
+ # @example Looking up format names
20
+ # Takagi::CoAP::Registries::ContentFormat.name_for(50) # => "application/json"
21
+ class ContentFormat < Base
22
+ # RFC 7252 §12.3 - Content-Format Registry
23
+ register(0, 'text/plain', :text_plain, rfc: 'RFC 7252 §12.3')
24
+ register(40, 'application/link-format', :link_format, rfc: 'RFC 6690')
25
+ register(41, 'application/xml', :xml, rfc: 'RFC 7252 §12.3')
26
+ register(42, 'application/octet-stream', :octet_stream, rfc: 'RFC 7252 §12.3')
27
+ register(47, 'application/exi', :exi, rfc: 'RFC 7252 §12.3')
28
+ register(50, 'application/json', :json, rfc: 'RFC 7252 §12.3')
29
+ register(60, 'application/cbor', :cbor, rfc: 'RFC 7049')
30
+
31
+ # Get MIME type for a content-format code
32
+ # @param code [Integer] Content-format code
33
+ # @return [String, nil] MIME type
34
+ def self.mime_type_for(code)
35
+ name_for(code)
36
+ end
37
+
38
+ # Get content-format code for a MIME type
39
+ # @param mime_type [String] MIME type
40
+ # @return [Integer, nil] Content-format code
41
+ def self.code_for_mime(mime_type)
42
+ value_for(mime_type)
43
+ end
44
+
45
+ # Check if format is JSON-based
46
+ # @param code [Integer] Content-format code
47
+ # @return [Boolean] true if JSON format
48
+ def self.json?(code)
49
+ mime = mime_type_for(code)
50
+ mime&.include?('json') || false
51
+ end
52
+
53
+ # Check if format is CBOR-based
54
+ # @param code [Integer] Content-format code
55
+ # @return [Boolean] true if CBOR format
56
+ def self.cbor?(code)
57
+ mime = mime_type_for(code)
58
+ mime&.include?('cbor') || false
59
+ end
60
+
61
+ # Check if format is text-based
62
+ # @param code [Integer] Content-format code
63
+ # @return [Boolean] true if text format
64
+ def self.text?(code)
65
+ mime = mime_type_for(code)
66
+ mime&.start_with?('text/') || false
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Takagi
4
+ module CoAP
5
+ module Registries
6
+ # CoAP Message Type Registry (RFC 7252 §3)
7
+ #
8
+ # Extensible registry for CoAP message types.
9
+ #
10
+ # @example Using predefined types
11
+ # Takagi::CoAP::Registries::MessageType::CONFIRMABLE # => 0
12
+ # Takagi::CoAP::Registries::MessageType::ACK # => 2
13
+ #
14
+ # @example Looking up type names
15
+ # Takagi::CoAP::Registries::MessageType.name_for(0) # => "Confirmable"
16
+ class MessageType < Base
17
+ # RFC 7252 §3 - Message Format
18
+ register(0, 'Confirmable', :confirmable, rfc: 'RFC 7252 §3')
19
+ register(1, 'Non-confirmable', :non_confirmable, rfc: 'RFC 7252 §3')
20
+ register(2, 'Acknowledgement', :acknowledgement, rfc: 'RFC 7252 §3')
21
+ register(3, 'Reset', :reset, rfc: 'RFC 7252 §3')
22
+
23
+ # Aliases for convenience
24
+ CON = CONFIRMABLE
25
+ NON = NON_CONFIRMABLE
26
+ ACK = ACKNOWLEDGEMENT
27
+ RST = RESET
28
+
29
+ # Check if type is confirmable
30
+ # @param type [Integer] Message type
31
+ # @return [Boolean] true if confirmable
32
+ def self.confirmable?(type)
33
+ type == CONFIRMABLE
34
+ end
35
+
36
+ # Check if type is non-confirmable
37
+ # @param type [Integer] Message type
38
+ # @return [Boolean] true if non-confirmable
39
+ def self.non_confirmable?(type)
40
+ type == NON_CONFIRMABLE
41
+ end
42
+
43
+ # Check if type is acknowledgement
44
+ # @param type [Integer] Message type
45
+ # @return [Boolean] true if acknowledgement
46
+ def self.acknowledgement?(type)
47
+ type == ACKNOWLEDGEMENT
48
+ end
49
+ class << self
50
+ alias ack? acknowledgement?
51
+ end
52
+
53
+ # Check if type is reset
54
+ # @param type [Integer] Message type
55
+ # @return [Boolean] true if reset
56
+ def self.reset?(type)
57
+ type == RESET
58
+ end
59
+
60
+ # Check if type is valid
61
+ # @param type [Integer] Message type
62
+ # @return [Boolean] true if valid
63
+ def self.valid?(type)
64
+ (0..3).include?(type)
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Takagi
4
+ module CoAP
5
+ module Registries
6
+ # CoAP Method Code Registry (RFC 7252 §12.1.1)
7
+ #
8
+ # Extensible registry for CoAP request method codes.
9
+ # Plugins can register custom methods without modifying core code.
10
+ #
11
+ # @example Using predefined methods
12
+ # Takagi::CoAP::Registries::Method::GET # => 1
13
+ # Takagi::CoAP::Registries::Method::POST # => 2
14
+ #
15
+ # @example Registering a custom method
16
+ # Takagi::CoAP::Registries::Method.register(5, 'FETCH', :fetch, rfc: 'RFC 8132')
17
+ # Takagi::CoAP::Registries::Method::FETCH # => 5
18
+ #
19
+ # @example Looking up method names
20
+ # Takagi::CoAP::Registries::Method.name_for(1) # => "GET"
21
+ class Method < Base
22
+ # RFC 7252 §5.8 - Method Codes
23
+ register(0, 'EMPTY', :empty, rfc: 'RFC 7252 §5.8')
24
+ register(1, 'GET', :get, rfc: 'RFC 7252 §5.8.1')
25
+ register(2, 'POST', :post, rfc: 'RFC 7252 §5.8.2')
26
+ register(3, 'PUT', :put, rfc: 'RFC 7252 §5.8.3')
27
+ register(4, 'DELETE', :delete, rfc: 'RFC 7252 §5.8.4')
28
+
29
+ # Check if code is a valid method code
30
+ # @param code [Integer] Code to check
31
+ # @return [Boolean] true if valid method
32
+ def self.valid?(code)
33
+ (0..31).include?(code) && registered?(code)
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Takagi
4
+ module CoAP
5
+ module Registries
6
+ # CoAP Option Number Registry (RFC 7252 §5.10)
7
+ #
8
+ # Extensible registry for CoAP option numbers.
9
+ # Plugins can register custom options without modifying core code.
10
+ #
11
+ # @example Using predefined options
12
+ # Takagi::CoAP::Registries::Option::URI_PATH # => 11
13
+ # Takagi::CoAP::Registries::Option::CONTENT_FORMAT # => 12
14
+ #
15
+ # @example Registering a custom option
16
+ # Takagi::CoAP::Registries::Option.register(65000, 'Custom-Option', :custom_option)
17
+ # Takagi::CoAP::Registries::Option::CUSTOM_OPTION # => 65000
18
+ #
19
+ # @example Looking up option names
20
+ # Takagi::CoAP::Registries::Option.name_for(11) # => "Uri-Path"
21
+ class Option < Base
22
+ # RFC 7252 §5.10.1 - Option Definitions
23
+ register(1, 'If-Match', :if_match, rfc: 'RFC 7252 §5.10.8.1')
24
+ register(3, 'Uri-Host', :uri_host, rfc: 'RFC 7252 §5.10.1')
25
+ register(4, 'ETag', :etag, rfc: 'RFC 7252 §5.10.6')
26
+ register(5, 'If-None-Match', :if_none_match, rfc: 'RFC 7252 §5.10.8.2')
27
+ register(6, 'Observe', :observe, rfc: 'RFC 7641 §2')
28
+ register(7, 'Uri-Port', :uri_port, rfc: 'RFC 7252 §5.10.1')
29
+ register(8, 'Location-Path', :location_path, rfc: 'RFC 7252 §5.10.7')
30
+ register(11, 'Uri-Path', :uri_path, rfc: 'RFC 7252 §5.10.1')
31
+ register(12, 'Content-Format', :content_format, rfc: 'RFC 7252 §5.10.3')
32
+ register(14, 'Max-Age', :max_age, rfc: 'RFC 7252 §5.10.5')
33
+ register(15, 'Uri-Query', :uri_query, rfc: 'RFC 7252 §5.10.2')
34
+ register(17, 'Accept', :accept, rfc: 'RFC 7252 §5.10.4')
35
+ register(20, 'Location-Query', :location_query, rfc: 'RFC 7252 §5.10.7')
36
+ register(35, 'Proxy-Uri', :proxy_uri, rfc: 'RFC 7252 §5.10.2')
37
+ register(39, 'Proxy-Scheme', :proxy_scheme, rfc: 'RFC 7252 §5.10.2')
38
+ register(60, 'Size1', :size1, rfc: 'RFC 7252 §5.10.9')
39
+
40
+ # Check if an option is critical
41
+ # Critical options must be understood by the recipient
42
+ # @param number [Integer] Option number
43
+ # @return [Boolean] true if critical
44
+ def self.critical?(number)
45
+ number.allbits?(1)
46
+ end
47
+
48
+ # Check if an option is unsafe to forward
49
+ # @param number [Integer] Option number
50
+ # @return [Boolean] true if unsafe
51
+ def self.unsafe?(number)
52
+ number.allbits?(2)
53
+ end
54
+
55
+ # Check if an option has NoCacheKey property
56
+ # @param number [Integer] Option number
57
+ # @return [Boolean] true if NoCacheKey
58
+ def self.no_cache_key?(number)
59
+ (number & 0x1E) == 0x1C
60
+ end
61
+
62
+ # Check if option number is valid
63
+ # @param number [Integer] Option number
64
+ # @return [Boolean] true if valid
65
+ def self.valid?(number)
66
+ number.between?(0, 65_535)
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,93 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Takagi
4
+ module CoAP
5
+ module Registries
6
+ # CoAP Response Code Registry (RFC 7252 §12.1.2)
7
+ #
8
+ # Extensible registry for CoAP response codes.
9
+ # Plugins can register custom response codes without modifying core code.
10
+ #
11
+ # @example Using predefined codes
12
+ # Takagi::CoAP::Registries::Response::CONTENT # => 69 (2.05)
13
+ # Takagi::CoAP::Registries::Response::NOT_FOUND # => 132 (4.04)
14
+ #
15
+ # @example Registering a custom code
16
+ # Takagi::CoAP::Registries::Response.register(231, '7.07 Custom', :custom)
17
+ # Takagi::CoAP::Registries::Response::CUSTOM # => 231
18
+ #
19
+ # @example Looking up code names
20
+ # Takagi::CoAP::Registries::Response.name_for(69) # => "2.05 Content"
21
+ class Response < Base
22
+ # Success 2.xx (RFC 7252 §5.9.1)
23
+ register(65, '2.01 Created', :created, rfc: 'RFC 7252 §5.9.1.1')
24
+ register(66, '2.02 Deleted', :deleted, rfc: 'RFC 7252 §5.9.1.2')
25
+ register(67, '2.03 Valid', :valid, rfc: 'RFC 7252 §5.9.1.3')
26
+ register(68, '2.04 Changed', :changed, rfc: 'RFC 7252 §5.9.1.4')
27
+ register(69, '2.05 Content', :content, rfc: 'RFC 7252 §5.9.1.5')
28
+
29
+ # Client Error 4.xx (RFC 7252 §5.9.2)
30
+ register(128, '4.00 Bad Request', :bad_request, rfc: 'RFC 7252 §5.9.2.1')
31
+ register(129, '4.01 Unauthorized', :unauthorized, rfc: 'RFC 7252 §5.9.2.2')
32
+ register(130, '4.02 Bad Option', :bad_option, rfc: 'RFC 7252 §5.9.2.3')
33
+ register(131, '4.03 Forbidden', :forbidden, rfc: 'RFC 7252 §5.9.2.4')
34
+ register(132, '4.04 Not Found', :not_found, rfc: 'RFC 7252 §5.9.2.5')
35
+ register(133, '4.05 Method Not Allowed', :method_not_allowed, rfc: 'RFC 7252 §5.9.2.6')
36
+ register(134, '4.06 Not Acceptable', :not_acceptable, rfc: 'RFC 7252 §5.9.2.7')
37
+ register(140, '4.12 Precondition Failed', :precondition_failed, rfc: 'RFC 7252 §5.9.2.9')
38
+ register(141, '4.13 Request Entity Too Large', :request_entity_too_large, rfc: 'RFC 7252 §5.9.2.10')
39
+ register(143, '4.15 Unsupported Content-Format', :unsupported_content_format, rfc: 'RFC 7252 §5.9.2.11')
40
+
41
+ # Server Error 5.xx (RFC 7252 §5.9.3)
42
+ register(160, '5.00 Internal Server Error', :internal_server_error, rfc: 'RFC 7252 §5.9.3.1')
43
+ register(161, '5.01 Not Implemented', :not_implemented, rfc: 'RFC 7252 §5.9.3.2')
44
+ register(162, '5.02 Bad Gateway', :bad_gateway, rfc: 'RFC 7252 §5.9.3.3')
45
+ register(163, '5.03 Service Unavailable', :service_unavailable, rfc: 'RFC 7252 §5.9.3.4')
46
+ register(164, '5.04 Gateway Timeout', :gateway_timeout, rfc: 'RFC 7252 §5.9.3.5')
47
+ register(165, '5.05 Proxying Not Supported', :proxying_not_supported, rfc: 'RFC 7252 §5.9.3.6')
48
+
49
+ # Get the response class (2, 4, 5, etc.)
50
+ # @param code [Integer] Response code
51
+ # @return [Integer] Class number
52
+ def self.class_for(code)
53
+ code / 32
54
+ end
55
+
56
+ # Check if code is a success (2.xx)
57
+ # @param code [Integer] Response code
58
+ # @return [Boolean] true if success
59
+ def self.success?(code)
60
+ class_for(code) == 2
61
+ end
62
+
63
+ # Check if code is a client error (4.xx)
64
+ # @param code [Integer] Response code
65
+ # @return [Boolean] true if client error
66
+ def self.client_error?(code)
67
+ class_for(code) == 4
68
+ end
69
+
70
+ # Check if code is a server error (5.xx)
71
+ # @param code [Integer] Response code
72
+ # @return [Boolean] true if server error
73
+ def self.server_error?(code)
74
+ class_for(code) == 5
75
+ end
76
+
77
+ # Check if code is any error (4.xx or 5.xx)
78
+ # @param code [Integer] Response code
79
+ # @return [Boolean] true if error
80
+ def self.error?(code)
81
+ client_error?(code) || server_error?(code)
82
+ end
83
+
84
+ # Check if code is a valid response code
85
+ # @param code [Integer] Code to check
86
+ # @return [Boolean] true if valid response code
87
+ def self.valid?(code)
88
+ code.between?(64, 191) && registered?(code)
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Takagi
4
+ module CoAP
5
+ module Registries
6
+ # CoAP Signaling Code Registry (RFC 8323 §5)
7
+ #
8
+ # Registry for CoAP over TCP/TLS signaling messages.
9
+ # Signaling messages use codes in the 7.xx range.
10
+ #
11
+ # @example Using signaling codes
12
+ # Takagi::CoAP::Registries::Signaling::CSM # => 225 (7.01)
13
+ # Takagi::CoAP::Registries::Signaling::PING # => 226 (7.02)
14
+ #
15
+ # @example Looking up signaling message names
16
+ # Takagi::CoAP::Registries::Signaling.name_for(225) # => "7.01 CSM"
17
+ class Signaling < Base
18
+ # RFC 8323 §5.3 - Signaling Codes
19
+ register(225, '7.01 CSM', :csm, rfc: 'RFC 8323 §5.3.1') # Capabilities and Settings Message
20
+ register(226, '7.02 Ping', :ping, rfc: 'RFC 8323 §5.3.2') # Ping
21
+ register(227, '7.03 Pong', :pong, rfc: 'RFC 8323 §5.3.3') # Pong
22
+ register(228, '7.04 Release', :release, rfc: 'RFC 8323 §5.3.4') # Release
23
+ register(229, '7.05 Abort', :abort, rfc: 'RFC 8323 §5.3.5') # Abort
24
+
25
+ # Check if code is a signaling code
26
+ # @param code [Integer] Code to check
27
+ # @return [Boolean] true if signaling code (7.xx)
28
+ def self.signaling?(code)
29
+ (224..255).include?(code) && registered?(code)
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'registries/signaling'
4
+
5
+ module Takagi
6
+ module CoAP
7
+ # Alias for Registries::Signaling
8
+ Signaling = Registries::Signaling
9
+ end
10
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Takagi
4
+ # CoAP Protocol implementation following RFC 7252 and related RFCs.
5
+ #
6
+ # This module provides a registry-based system for CoAP protocol constants.
7
+ # Plugins and extensions can register additional constants without modifying
8
+ # core code by using the registries under {Takagi::CoAP::Registries}.
9
+ #
10
+ # @example Registering a custom response code
11
+ # Takagi::CoAP::Registries::Response.register(231, '7.01 Custom Code', :custom_code)
12
+ #
13
+ # @example Using registered constants
14
+ # code = Takagi::CoAP::Registries::Response::CONTENT # => 69
15
+ # Takagi::CoAP::Registries::Response.name_for(69) # => "2.05 Content"
16
+ module CoAP
17
+ # Default CoAP port (RFC 7252 §6.1)
18
+ DEFAULT_PORT = 5683
19
+
20
+ # Default CoAPS (secure) port (RFC 7252 §6.2)
21
+ DEFAULT_SECURE_PORT = 5684
22
+
23
+ # CoAP version (RFC 7252 §3)
24
+ VERSION = 1
25
+ end
26
+ end
27
+
28
+ # Load registries
29
+ require_relative 'coap/registries/base'
30
+ require_relative 'coap/registries/method'
31
+ require_relative 'coap/registries/response'
32
+ require_relative 'coap/registries/option'
33
+ require_relative 'coap/registries/content_format'
34
+ require_relative 'coap/registries/message_type'
35
+ require_relative 'coap/registries/signaling'
36
+
37
+ require_relative 'coap/code_helpers'