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,217 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Example: Cloud Gateway Application (Modular Pattern)
5
+ #
6
+ # This example demonstrates the modular pattern using Takagi::Controller
7
+ # and Takagi::Application for high-scale cloud/gateway servers.
8
+ #
9
+ # Use case: Cloud server handling thousands of devices with uneven load
10
+ #
11
+ # Run with: ruby examples/cloud_gateway_app.rb
12
+
13
+ require_relative '../lib/takagi'
14
+
15
+ # High-throughput telemetry ingestion
16
+ class TelemetryController < Takagi::Controller
17
+ configure do
18
+ mount '/telemetry'
19
+ profile :high_throughput # 8 processes × 4 threads = 32 workers
20
+ end
21
+
22
+ # POST /telemetry/data - High-volume sensor data ingestion
23
+ post '/data' do |request|
24
+ data = request.data
25
+
26
+ # Simulate data processing
27
+ {
28
+ received: true,
29
+ device_id: data['device_id'],
30
+ timestamp: Time.now.to_i,
31
+ points: data['points']&.length || 0
32
+ }
33
+ end
34
+
35
+ # GET /telemetry/stats - Telemetry statistics
36
+ get '/stats' do
37
+ {
38
+ total_devices: 1523,
39
+ points_per_second: 15_234,
40
+ avg_latency_ms: 12
41
+ }
42
+ end
43
+ end
44
+
45
+ # Observable streams for real-time monitoring
46
+ class ObservableController < Takagi::Controller
47
+ configure do
48
+ mount '/observe'
49
+ profile :long_lived # 2 processes × 8 threads = 16 workers (for long connections)
50
+ end
51
+
52
+ # OBSERVE /observe/devices/:id - Stream device data
53
+ observable '/devices/:id' do |_request, params|
54
+ device_id = params[:id]
55
+
56
+ {
57
+ device_id: device_id,
58
+ status: 'online',
59
+ last_seen: Time.now.to_i,
60
+ temperature: rand(20..30),
61
+ battery: rand(50..100)
62
+ }
63
+ end
64
+
65
+ # OBSERVE /observe/alerts - Stream system alerts
66
+ observable '/alerts' do
67
+ {
68
+ timestamp: Time.now.to_i,
69
+ severity: %w[info warning error].sample,
70
+ message: 'Sample alert message'
71
+ }
72
+ end
73
+ end
74
+
75
+ # Device management API
76
+ class DeviceManagementController < Takagi::Controller
77
+ configure do
78
+ mount '/devices'
79
+ profile :low_traffic # 1 process × 2 threads = 2 workers
80
+ end
81
+
82
+ # GET /devices - List all devices
83
+ get '/' do
84
+ {
85
+ devices: [
86
+ { id: 'device-001', name: 'Sensor 1', status: 'online' },
87
+ { id: 'device-002', name: 'Sensor 2', status: 'online' },
88
+ { id: 'device-003', name: 'Sensor 3', status: 'offline' }
89
+ ],
90
+ total: 3
91
+ }
92
+ end
93
+
94
+ # GET /devices/:id - Get device details
95
+ get '/:id' do |_request, params|
96
+ {
97
+ id: params[:id],
98
+ name: "Device #{params[:id]}",
99
+ status: 'online',
100
+ firmware: '2.1.0',
101
+ last_seen: Time.now.to_i
102
+ }
103
+ end
104
+
105
+ # POST /devices/:id/command - Send command to device
106
+ post '/:id/command' do |request, params|
107
+ command = request.data
108
+
109
+ {
110
+ device_id: params[:id],
111
+ command: command['action'],
112
+ status: 'queued'
113
+ }
114
+ end
115
+ end
116
+
117
+ # Configuration API
118
+ class ConfigController < Takagi::Controller
119
+ configure do
120
+ mount '/config'
121
+ profile :low_traffic
122
+ end
123
+
124
+ # GET /config/global - Get global configuration
125
+ get '/global' do
126
+ {
127
+ telemetry_interval: 60,
128
+ max_devices: 10_000,
129
+ retention_days: 30
130
+ }
131
+ end
132
+
133
+ # PUT /config/global - Update global configuration
134
+ put '/global' do |request|
135
+ config = request.data
136
+
137
+ {
138
+ updated: true,
139
+ config: config
140
+ }
141
+ end
142
+ end
143
+
144
+ # Firmware update API (large payloads)
145
+ class FirmwareController < Takagi::Controller
146
+ configure do
147
+ mount '/firmware'
148
+ profile :large_payloads # 2 processes × 2 threads, 10MB buffer
149
+ end
150
+
151
+ # GET /firmware/latest - Get latest firmware info
152
+ get '/latest' do
153
+ {
154
+ version: '2.1.0',
155
+ size_bytes: 5_242_880,
156
+ checksum: 'abc123...',
157
+ release_date: '2025-01-15'
158
+ }
159
+ end
160
+
161
+ # POST /firmware/upload - Upload new firmware
162
+ post '/upload' do |request|
163
+ # Simulate firmware processing
164
+ {
165
+ uploaded: true,
166
+ size: request.payload&.bytesize || 0,
167
+ checksum: 'processing...'
168
+ }
169
+ end
170
+ end
171
+
172
+ # Main application
173
+ class CloudGatewayApp < Takagi::Application
174
+ configure do
175
+ load_controllers(
176
+ TelemetryController,
177
+ ObservableController,
178
+ DeviceManagementController,
179
+ ConfigController,
180
+ FirmwareController
181
+ )
182
+ end
183
+ end
184
+
185
+ if __FILE__ == $PROGRAM_NAME
186
+ puts "Starting Cloud Gateway App..."
187
+ puts "CoAP server running on port 5683"
188
+ puts ""
189
+ puts "Architecture:"
190
+ puts " /telemetry - High throughput (32 workers) - Sensor data ingestion"
191
+ puts " /observe - Long-lived (16 workers) - Real-time streams"
192
+ puts " /devices - Low traffic (2 workers) - Device management"
193
+ puts " /config - Low traffic (2 workers) - Configuration API"
194
+ puts " /firmware - Large payloads (4 workers) - Firmware updates"
195
+ puts ""
196
+ puts "Example commands:"
197
+ puts " # High-volume telemetry"
198
+ puts " coap-client -m post coap://localhost/telemetry/data -e '{\"device_id\":\"dev-001\",\"points\":[{\"temp\":25}]}'"
199
+ puts ""
200
+ puts " # Observable streams"
201
+ puts " coap-client -m get -s 10 coap://localhost/observe/devices/dev-001"
202
+ puts " coap-client -m get -s 10 coap://localhost/observe/alerts"
203
+ puts ""
204
+ puts " # Device management"
205
+ puts " coap-client -m get coap://localhost/devices"
206
+ puts " coap-client -m get coap://localhost/devices/dev-001"
207
+ puts " coap-client -m post coap://localhost/devices/dev-001/command -e '{\"action\":\"reboot\"}'"
208
+ puts ""
209
+ puts " # Configuration"
210
+ puts " coap-client -m get coap://localhost/config/global"
211
+ puts ""
212
+ puts " # Firmware"
213
+ puts " coap-client -m get coap://localhost/firmware/latest"
214
+ puts ""
215
+
216
+ CloudGatewayApp.run!(port: 5683, protocols: [:udp, :tcp])
217
+ end
@@ -0,0 +1,258 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Example: Nested API Application
5
+ #
6
+ # This example demonstrates nested controller mounting for
7
+ # organizing complex APIs with versioning and resource hierarchy.
8
+ #
9
+ # Use case: RESTful API with nested resources
10
+ #
11
+ # Run with: ruby examples/nested_api_app.rb
12
+
13
+ require_relative '../lib/takagi'
14
+
15
+ # --- API v1 ---
16
+
17
+ # Sensors controller under API v1
18
+ class V1SensorsController < Takagi::Controller
19
+ configure do
20
+ mount '/sensors'
21
+ profile :high_throughput
22
+ end
23
+
24
+ # GET /api/v1/sensors
25
+ get '/' do
26
+ {
27
+ sensors: [
28
+ { id: 'temp-001', type: 'temperature', location: 'room-1' },
29
+ { id: 'hum-001', type: 'humidity', location: 'room-1' }
30
+ ]
31
+ }
32
+ end
33
+
34
+ # GET /api/v1/sensors/:id
35
+ get '/:id' do |_request, params|
36
+ {
37
+ id: params[:id],
38
+ type: 'temperature',
39
+ value: rand(20..30),
40
+ unit: 'celsius',
41
+ timestamp: Time.now.to_i
42
+ }
43
+ end
44
+
45
+ # POST /api/v1/sensors/:id/readings
46
+ post '/:id/readings' do |request, params|
47
+ data = request.data
48
+
49
+ {
50
+ sensor_id: params[:id],
51
+ reading_id: SecureRandom.uuid,
52
+ value: data['value'],
53
+ stored: true
54
+ }
55
+ end
56
+ end
57
+
58
+ # Users controller under API v1
59
+ class V1UsersController < Takagi::Controller
60
+ configure do
61
+ mount '/users'
62
+ profile :low_traffic
63
+ end
64
+
65
+ # GET /api/v1/users
66
+ get '/' do
67
+ {
68
+ users: [
69
+ { id: 'user-001', name: 'Alice', role: 'admin' },
70
+ { id: 'user-002', name: 'Bob', role: 'user' }
71
+ ]
72
+ }
73
+ end
74
+
75
+ # GET /api/v1/users/:id
76
+ get '/:id' do |_request, params|
77
+ {
78
+ id: params[:id],
79
+ name: "User #{params[:id]}",
80
+ role: 'user',
81
+ created_at: Time.now.to_i
82
+ }
83
+ end
84
+ end
85
+
86
+ # API v1 parent controller
87
+ class ApiV1Controller < Takagi::Controller
88
+ configure do
89
+ mount '/api/v1'
90
+
91
+ # Nest resource controllers
92
+ nest V1SensorsController, V1UsersController
93
+ end
94
+
95
+ # GET /api/v1 - API info
96
+ get '/' do
97
+ {
98
+ version: '1.0',
99
+ endpoints: {
100
+ sensors: '/api/v1/sensors',
101
+ users: '/api/v1/users'
102
+ },
103
+ status: 'stable'
104
+ }
105
+ end
106
+ end
107
+
108
+ # --- API v2 (Improved version) ---
109
+
110
+ class V2SensorsController < Takagi::Controller
111
+ configure do
112
+ mount '/sensors'
113
+ profile :high_throughput
114
+ end
115
+
116
+ # GET /api/v2/sensors
117
+ get '/' do
118
+ {
119
+ sensors: [
120
+ {
121
+ id: 'temp-001',
122
+ type: 'temperature',
123
+ location: { building: 'A', floor: 1, room: 'room-1' },
124
+ metadata: { calibrated: true, accuracy: 0.1 }
125
+ }
126
+ ],
127
+ pagination: { page: 1, total: 1 }
128
+ }
129
+ end
130
+
131
+ # GET /api/v2/sensors/:id
132
+ get '/:id' do |_request, params|
133
+ {
134
+ id: params[:id],
135
+ type: 'temperature',
136
+ readings: {
137
+ current: rand(20..30),
138
+ min_24h: 18,
139
+ max_24h: 32,
140
+ avg_24h: 25
141
+ },
142
+ unit: 'celsius',
143
+ timestamp: Time.now.to_i
144
+ }
145
+ end
146
+ end
147
+
148
+ class ApiV2Controller < Takagi::Controller
149
+ configure do
150
+ mount '/api/v2'
151
+ nest V2SensorsController
152
+ end
153
+
154
+ # GET /api/v2 - API info
155
+ get '/' do
156
+ {
157
+ version: '2.0',
158
+ endpoints: {
159
+ sensors: '/api/v2/sensors'
160
+ },
161
+ status: 'beta',
162
+ improvements: [
163
+ 'Enhanced sensor metadata',
164
+ 'Improved response structure',
165
+ 'Better pagination'
166
+ ]
167
+ }
168
+ end
169
+ end
170
+
171
+ # --- Admin API (separate hierarchy) ---
172
+
173
+ class AdminDevicesController < Takagi::Controller
174
+ configure do
175
+ mount '/devices'
176
+ end
177
+
178
+ # DELETE /admin/devices/:id
179
+ delete '/:id' do |_request, params|
180
+ {
181
+ deleted: true,
182
+ device_id: params[:id],
183
+ timestamp: Time.now.to_i
184
+ }
185
+ end
186
+ end
187
+
188
+ class AdminController < Takagi::Controller
189
+ configure do
190
+ mount '/admin'
191
+ profile :low_traffic
192
+ nest AdminDevicesController
193
+ end
194
+
195
+ # GET /admin/stats
196
+ get '/stats' do
197
+ {
198
+ total_devices: 523,
199
+ active_users: 42,
200
+ api_requests_24h: 15_234,
201
+ storage_used_gb: 125
202
+ }
203
+ end
204
+ end
205
+
206
+ # Main application
207
+ class NestedApiApp < Takagi::Application
208
+ configure do
209
+ load_controllers(
210
+ ApiV1Controller,
211
+ ApiV2Controller,
212
+ AdminController
213
+ )
214
+ end
215
+ end
216
+
217
+ if __FILE__ == $PROGRAM_NAME
218
+ puts "Starting Nested API App..."
219
+ puts "CoAP server running on port 5683"
220
+ puts ""
221
+ puts "API Structure:"
222
+ puts " /api/v1"
223
+ puts " /api/v1/sensors"
224
+ puts " GET /api/v1/sensors"
225
+ puts " GET /api/v1/sensors/:id"
226
+ puts " POST /api/v1/sensors/:id/readings"
227
+ puts " /api/v1/users"
228
+ puts " GET /api/v1/users"
229
+ puts " GET /api/v1/users/:id"
230
+ puts ""
231
+ puts " /api/v2 (Beta)"
232
+ puts " /api/v2/sensors"
233
+ puts " GET /api/v2/sensors"
234
+ puts " GET /api/v2/sensors/:id"
235
+ puts ""
236
+ puts " /admin"
237
+ puts " GET /admin/stats"
238
+ puts " /admin/devices"
239
+ puts " DELETE /admin/devices/:id"
240
+ puts ""
241
+ puts "Example commands:"
242
+ puts " # API v1"
243
+ puts " coap-client -m get coap://localhost/api/v1"
244
+ puts " coap-client -m get coap://localhost/api/v1/sensors"
245
+ puts " coap-client -m get coap://localhost/api/v1/sensors/temp-001"
246
+ puts " coap-client -m post coap://localhost/api/v1/sensors/temp-001/readings -e '{\"value\":25.5}'"
247
+ puts ""
248
+ puts " # API v2 (Beta)"
249
+ puts " coap-client -m get coap://localhost/api/v2"
250
+ puts " coap-client -m get coap://localhost/api/v2/sensors/temp-001"
251
+ puts ""
252
+ puts " # Admin"
253
+ puts " coap-client -m get coap://localhost/admin/stats"
254
+ puts " coap-client -m delete coap://localhost/admin/devices/old-device-001"
255
+ puts ""
256
+
257
+ NestedApiApp.run!(port: 5683, protocols: [:udp])
258
+ end
@@ -0,0 +1,71 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Example: Simple Device Application (IoT Device Pattern)
5
+ #
6
+ # This example demonstrates the simple pattern using Takagi::Base
7
+ # for constrained IoT devices with minimal resources.
8
+ #
9
+ # Use case: Raspberry Pi, ESP32, or edge device with 1-10 endpoints
10
+ #
11
+ # Run with: ruby examples/simple_device_app.rb
12
+
13
+ require_relative '../lib/takagi'
14
+
15
+ # Simple device application using global router pattern
16
+ class DeviceApp < Takagi::Base
17
+ # GET /sensor - Read sensor data
18
+ get '/sensor' do
19
+ {
20
+ temperature: 25.5,
21
+ humidity: 60,
22
+ timestamp: Time.now.to_i
23
+ }
24
+ end
25
+
26
+ # POST /command - Execute device command
27
+ post '/command' do |request|
28
+ command = request.data
29
+
30
+ case command['action']
31
+ when 'reboot'
32
+ { status: 'rebooting' }
33
+ when 'update'
34
+ { status: 'updating' }
35
+ else
36
+ { status: 'unknown_command' }
37
+ end
38
+ end
39
+
40
+ # OBSERVE /status - Stream device status (CoAP Observe)
41
+ observable '/status' do
42
+ {
43
+ online: true,
44
+ uptime: Process.clock_gettime(Process::CLOCK_MONOTONIC).to_i,
45
+ memory_used: `ps -o rss= -p #{Process.pid}`.to_i
46
+ }
47
+ end
48
+
49
+ # GET /config - Device configuration
50
+ get '/config' do
51
+ {
52
+ device_id: 'device-001',
53
+ firmware_version: '1.0.0',
54
+ update_interval: 60
55
+ }
56
+ end
57
+ end
58
+
59
+ if __FILE__ == $PROGRAM_NAME
60
+ puts "Starting Simple Device App..."
61
+ puts "CoAP server running on port 5683"
62
+ puts ""
63
+ puts "Try these commands:"
64
+ puts " coap-client -m get coap://localhost/sensor"
65
+ puts " coap-client -m post coap://localhost/command -e '{\"action\":\"reboot\"}'"
66
+ puts " coap-client -m get -s 10 coap://localhost/status # Observe for 10s"
67
+ puts " coap-client -m get coap://localhost/config"
68
+ puts ""
69
+
70
+ DeviceApp.run!(port: 5683, protocols: [:udp])
71
+ end
@@ -0,0 +1,138 @@
1
+ # Takagi Configuration Example
2
+ # Place this file as takagi.yml in your project root
3
+
4
+ # ============================================
5
+ # Server Settings
6
+ # ============================================
7
+ port: 5683
8
+ bind_address: "0.0.0.0" # Bind to all interfaces (default)
9
+ # Use "127.0.0.1" for local-only
10
+ # Use specific IP for single interface
11
+ server_name: "Takagi CoAP Server"
12
+
13
+ # Number of processes (for multi-process mode)
14
+ processes: 1
15
+
16
+ # Number of threads per process
17
+ threads: 4
18
+
19
+ # Supported protocols
20
+ protocols:
21
+ - udp
22
+ - tcp
23
+
24
+ # ============================================
25
+ # Logger Configuration
26
+ # ============================================
27
+ logger:
28
+ level: info # debug, info, warn, error
29
+ output: stdout
30
+
31
+ # ============================================
32
+ # Observability
33
+ # ============================================
34
+ observability:
35
+ backends:
36
+ - memory
37
+ # - prometheus # Uncomment for Prometheus metrics
38
+ # - statsd # Uncomment for StatsD metrics
39
+
40
+ # ============================================
41
+ # EventBus Configuration
42
+ # ============================================
43
+ event_bus:
44
+ # Worker pool size for async message delivery
45
+ # Higher values = more concurrent message processing
46
+ # Lower values = less memory/CPU usage
47
+ ractors: 10
48
+
49
+ # Current state cache (LRUCache)
50
+ # Stores latest value per address for fast lookups
51
+ state_cache_size: 1000 # Max number of addresses
52
+ state_cache_ttl: 3600 # Seconds (1 hour)
53
+
54
+ # Observer cleanup settings
55
+ # Removes stale CoAP observers periodically
56
+ cleanup_interval: 60 # Seconds between cleanup runs
57
+ max_observer_age: 600 # Seconds (10 minutes)
58
+
59
+ # ==========================================
60
+ # Message Buffering (for reliability)
61
+ # ==========================================
62
+ # Enable to buffer messages for late subscribers and reconnections
63
+ # Recommended for unreliable networks (WiFi, mesh, mobile)
64
+
65
+ message_buffering_enabled: false # Set to true to enable
66
+
67
+ # When enabled:
68
+ message_buffer_max_messages: 100 # Messages per address
69
+ message_buffer_ttl: 300 # Seconds (5 minutes)
70
+
71
+ # ============================================
72
+ # Router Configuration
73
+ # ============================================
74
+ router:
75
+ # Default Content-Format when not specified
76
+ # 0 = text/plain
77
+ # 40 = application/link-format
78
+ # 41 = application/xml
79
+ # 42 = application/octet-stream
80
+ # 50 = application/json (default)
81
+ # 60 = application/cbor
82
+ default_content_format: 50
83
+
84
+ # ============================================
85
+ # Middleware Configuration
86
+ # ============================================
87
+ middleware:
88
+ # Enable/disable middleware globally
89
+ enabled: true
90
+
91
+ # Middleware stack (executed in order)
92
+ # Middleware wraps request/response processing
93
+ # First middleware in list runs first (outermost layer)
94
+ stack:
95
+ # Simple middleware (no options)
96
+ - Logging
97
+
98
+ # Middleware with options
99
+ - name: Caching
100
+ options:
101
+ ttl: 300 # Cache TTL in seconds
102
+
103
+ # Available built-in middleware:
104
+ # - Debugging: Debug logging for requests/responses
105
+ # - Logging: Request/response logging
106
+ # - Caching: Simple in-memory cache
107
+ # - RateLimiting: Rate limit requests (options: max_requests, window)
108
+ # - Metrics: Collect metrics (options: backend)
109
+ # - Authentication: Token-based auth (options: tokens, realm)
110
+
111
+ # Example: Rate Limiting
112
+ # - name: RateLimiting
113
+ # options:
114
+ # max_requests: 100
115
+ # window: 60 # seconds
116
+
117
+ # Example: Authentication
118
+ # - name: Authentication
119
+ # options:
120
+ # tokens:
121
+ # - "secret-token-1"
122
+ # - "secret-token-2"
123
+ # realm: "CoAP API"
124
+
125
+ # Example: Custom middleware (full class name)
126
+ # - name: MyApp::CustomMiddleware
127
+ # options:
128
+ # custom_option: value
129
+
130
+ # ============================================
131
+ # Custom Application Settings
132
+ # ============================================
133
+ # Add any custom settings here - accessible via Takagi.config[:key]
134
+ custom:
135
+ # Example custom settings
136
+ app_name: "My IoT Application"
137
+ max_devices: 100
138
+ device_timeout: 30