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.
- checksums.yaml +4 -4
- data/.rubocop.yml +70 -7
- data/.yard/templates/default/layout/html/layout.erb +34 -0
- data/AGENTS.md +16 -0
- data/CHANGELOG.md +158 -1
- data/CODE_OF_CONDUCT.md +1 -1
- data/README.md +590 -23
- data/ROADMAP.md +55 -0
- data/Rakefile +4 -4
- data/Steepfile +39 -0
- data/bin/takagi-dev +159 -0
- data/docs/FIRST_PLUGIN_GUIDE.md +224 -0
- data/docs/HOOKS.md +31 -0
- data/examples/client_lifecycle_example.rb +118 -0
- data/examples/cloud_gateway_app.rb +217 -0
- data/examples/nested_api_app.rb +258 -0
- data/examples/simple_device_app.rb +71 -0
- data/examples/takagi.yml +138 -0
- data/lib/takagi/application.rb +256 -0
- data/lib/takagi/base/middleware_management.rb +39 -0
- data/lib/takagi/base/plugin_management.rb +75 -0
- data/lib/takagi/base/reactor_management.rb +104 -0
- data/lib/takagi/base/server_lifecycle.rb +156 -0
- data/lib/takagi/base.rb +103 -11
- data/lib/takagi/branding.rb +88 -0
- data/lib/takagi/cbor/decoder.rb +385 -0
- data/lib/takagi/cbor/encoder.rb +260 -0
- data/lib/takagi/cbor/error.rb +17 -0
- data/lib/takagi/cbor/version.rb +9 -0
- data/lib/takagi/client/response.rb +236 -0
- data/lib/takagi/client.rb +265 -0
- data/lib/takagi/client_base.rb +204 -0
- data/lib/takagi/coap/code_helpers.rb +190 -0
- data/lib/takagi/coap/registries/base.rb +165 -0
- data/lib/takagi/coap/registries/content_format.rb +71 -0
- data/lib/takagi/coap/registries/message_type.rb +69 -0
- data/lib/takagi/coap/registries/method.rb +38 -0
- data/lib/takagi/coap/registries/option.rb +71 -0
- data/lib/takagi/coap/registries/response.rb +93 -0
- data/lib/takagi/coap/registries/signaling.rb +34 -0
- data/lib/takagi/coap/signaling.rb +10 -0
- data/lib/takagi/coap.rb +37 -0
- data/lib/takagi/composite_router.rb +186 -0
- data/lib/takagi/config.rb +337 -0
- data/lib/takagi/controller/resource_allocator.rb +164 -0
- data/lib/takagi/controller/thread_pool.rb +144 -0
- data/lib/takagi/controller.rb +319 -0
- data/lib/takagi/core/attribute_set.rb +128 -0
- data/lib/takagi/discovery/core_link_format.rb +137 -0
- data/lib/takagi/errors.rb +536 -0
- data/lib/takagi/event_bus/address_prefix.rb +142 -0
- data/lib/takagi/event_bus/async_executor.rb +235 -0
- data/lib/takagi/event_bus/coap_bridge.rb +208 -0
- data/lib/takagi/event_bus/future.rb +153 -0
- data/lib/takagi/event_bus/lru_cache.rb +157 -0
- data/lib/takagi/event_bus/message_buffer.rb +237 -0
- data/lib/takagi/event_bus/observer_cleanup.rb +110 -0
- data/lib/takagi/event_bus/scope.rb +74 -0
- data/lib/takagi/event_bus.rb +594 -0
- data/lib/takagi/helpers.rb +88 -0
- data/lib/takagi/hooks.rb +82 -0
- data/lib/takagi/initializer.rb +18 -0
- data/lib/takagi/logger.rb +15 -6
- data/lib/takagi/message/base.rb +155 -0
- data/lib/takagi/message/deduplication_cache.rb +84 -0
- data/lib/takagi/message/inbound.rb +147 -0
- data/lib/takagi/message/outbound.rb +223 -0
- data/lib/takagi/message/request.rb +158 -0
- data/lib/takagi/message/retransmission_manager.rb +193 -0
- data/lib/takagi/middleware/authentication.rb +19 -0
- data/lib/takagi/middleware/caching.rb +23 -0
- data/lib/takagi/middleware/debugging.rb +16 -0
- data/lib/takagi/middleware/logging.rb +14 -0
- data/lib/takagi/middleware/metrics.rb +440 -0
- data/lib/takagi/middleware/rate_limiting.rb +24 -0
- data/lib/takagi/middleware_stack.rb +166 -0
- data/lib/takagi/network/base.rb +76 -0
- data/lib/takagi/network/framing/tcp.rb +222 -0
- data/lib/takagi/network/framing/udp.rb +110 -0
- data/lib/takagi/network/registry.rb +72 -0
- data/lib/takagi/network/tcp.rb +60 -0
- data/lib/takagi/network/tcp_sender.rb +21 -0
- data/lib/takagi/network/udp.rb +61 -0
- data/lib/takagi/network/udp_sender.rb +20 -0
- data/lib/takagi/observable/emitter.rb +62 -0
- data/lib/takagi/observable/reactor.rb +488 -0
- data/lib/takagi/observable/registry.rb +122 -0
- data/lib/takagi/observe_registry.rb +10 -0
- data/lib/takagi/observer/client.rb +68 -0
- data/lib/takagi/observer/registry.rb +137 -0
- data/lib/takagi/observer/sender.rb +39 -0
- data/lib/takagi/observer/watcher.rb +43 -0
- data/lib/takagi/plugin.rb +313 -0
- data/lib/takagi/profiles.rb +176 -0
- data/lib/takagi/reactor.rb +23 -0
- data/lib/takagi/reactor_registry.rb +64 -0
- data/lib/takagi/registry/base.rb +268 -0
- data/lib/takagi/response_builder.rb +141 -0
- data/lib/takagi/router/metadata_extractor.rb +133 -0
- data/lib/takagi/router/route_matcher.rb +83 -0
- data/lib/takagi/router.rb +284 -25
- data/lib/takagi/serialization/base.rb +102 -0
- data/lib/takagi/serialization/cbor_serializer.rb +92 -0
- data/lib/takagi/serialization/json_serializer.rb +96 -0
- data/lib/takagi/serialization/octet_stream_serializer.rb +82 -0
- data/lib/takagi/serialization/registry.rb +187 -0
- data/lib/takagi/serialization/text_serializer.rb +87 -0
- data/lib/takagi/serialization.rb +117 -0
- data/lib/takagi/server/multi.rb +41 -0
- data/lib/takagi/server/registry.rb +71 -0
- data/lib/takagi/server/tcp.rb +249 -0
- data/lib/takagi/server/udp.rb +139 -0
- data/lib/takagi/server/udp_worker.rb +174 -0
- data/lib/takagi/server.rb +1 -31
- data/lib/takagi/server_registry.rb +10 -0
- data/lib/takagi/tcp_client.rb +142 -0
- data/lib/takagi/version.rb +2 -1
- data/lib/takagi.rb +24 -3
- data/sig/takagi/application.rbs +48 -0
- data/sig/takagi/base/middleware_management.rbs +33 -0
- data/sig/takagi/base/reactor_management.rbs +52 -0
- data/sig/takagi/base/server_lifecycle.rbs +54 -0
- data/sig/takagi/base.rbs +48 -0
- data/sig/takagi/cbor/decoder.rbs +171 -0
- data/sig/takagi/cbor/encoder.rbs +146 -0
- data/sig/takagi/cbor/error.rbs +19 -0
- data/sig/takagi/cbor/version.rbs +7 -0
- data/sig/takagi/client/response.rbs +148 -0
- data/sig/takagi/client.rbs +119 -0
- data/sig/takagi/client_base.rbs +135 -0
- data/sig/takagi/coap/code_helpers.rbs +91 -0
- data/sig/takagi/coap/registries/base.rbs +95 -0
- data/sig/takagi/coap/registries/content_format.rbs +47 -0
- data/sig/takagi/coap/registries/message_type.rbs +53 -0
- data/sig/takagi/coap/registries/method.rbs +27 -0
- data/sig/takagi/coap/registries/option.rbs +43 -0
- data/sig/takagi/coap/registries/response.rbs +52 -0
- data/sig/takagi/coap.rbs +24 -0
- data/sig/takagi/composite_router.rbs +46 -0
- data/sig/takagi/config.rbs +134 -0
- data/sig/takagi/controller.rbs +73 -0
- data/sig/takagi/core/attribute_set.rbs +57 -0
- data/sig/takagi/discovery/core_link_format.rbs +50 -0
- data/sig/takagi/event_bus/address_prefix.rbs +78 -0
- data/sig/takagi/event_bus/async_executor.rbs +88 -0
- data/sig/takagi/event_bus/coap_bridge.rbs +93 -0
- data/sig/takagi/event_bus/future.rbs +78 -0
- data/sig/takagi/event_bus/lru_cache.rbs +86 -0
- data/sig/takagi/event_bus/message_buffer.rbs +133 -0
- data/sig/takagi/event_bus/observer_cleanup.rbs +62 -0
- data/sig/takagi/event_bus.rbs +320 -0
- data/sig/takagi/helpers.rbs +34 -0
- data/sig/takagi/initializer.rbs +9 -0
- data/sig/takagi/logger.rbs +17 -0
- data/sig/takagi/message/base.rbs +64 -0
- data/sig/takagi/message/deduplication_cache.rbs +49 -0
- data/sig/takagi/message/inbound.rbs +76 -0
- data/sig/takagi/message/outbound.rbs +48 -0
- data/sig/takagi/message/request.rbs +32 -0
- data/sig/takagi/message/retransmission_manager.rbs +76 -0
- data/sig/takagi/middleware/authentication.rbs +11 -0
- data/sig/takagi/middleware/caching.rbs +13 -0
- data/sig/takagi/middleware/debugging.rbs +9 -0
- data/sig/takagi/middleware/logging.rbs +7 -0
- data/sig/takagi/middleware/metrics.rbs +15 -0
- data/sig/takagi/middleware/rate_limiting.rbs +13 -0
- data/sig/takagi/middleware_stack.rbs +69 -0
- data/sig/takagi/network/tcp_sender.rbs +10 -0
- data/sig/takagi/network/udp_sender.rbs +14 -0
- data/sig/takagi/observe_registry.rbs +36 -0
- data/sig/takagi/observer/client.rbs +36 -0
- data/sig/takagi/observer/sender.rbs +12 -0
- data/sig/takagi/observer/watcher.rbs +18 -0
- data/sig/takagi/profiles.rbs +33 -0
- data/sig/takagi/reactor.rbs +20 -0
- data/sig/takagi/reactor_registry.rbs +14 -0
- data/sig/takagi/response_builder.rbs +12 -0
- data/sig/takagi/router/metadata_extractor.rbs +71 -0
- data/sig/takagi/router/route_matcher.rbs +43 -0
- data/sig/takagi/router.rbs +166 -0
- data/sig/takagi/serialization.rbs +32 -0
- data/sig/takagi/server/multi.rbs +16 -0
- data/sig/takagi/server/tcp.rbs +42 -0
- data/sig/takagi/server/udp.rbs +52 -0
- data/sig/takagi/server/udp_worker.rbs +42 -0
- data/sig/takagi/server.rbs +4 -0
- data/sig/takagi/server_registry.rbs +71 -0
- data/sig/takagi/tcp_client.rbs +23 -0
- data/sig/takagi/version.rbs +5 -0
- data/takagi.gemspec +37 -35
- metadata +204 -31
- data/.idea/.gitignore +0 -8
- data/.idea/misc.xml +0 -4
- data/.idea/modules.xml +0 -8
- data/.idea/takagi.iml +0 -81
- data/.idea/vcs.xml +0 -6
- 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
|
data/examples/takagi.yml
ADDED
|
@@ -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
|