eywa-client 0.3.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 +7 -0
- data/LICENSE +21 -0
- data/README.md +370 -0
- data/lib/eywa.rb +242 -0
- metadata +92 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 3cb389c11a653f8a4d9b9e73d49619c2438b62dd12dd61ce0f523541d348f24d
|
|
4
|
+
data.tar.gz: 54cfff0b41310d46354f93ea02f6f9ff022f260e7afff99398533fe21782fe88
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: d4e3e0bbced5016f5b293222a6e8761b0b2f9f7fd13e2fd0e862fbe9b705db9887ad649c47a2a073ef4ee07f2f5fa5f0e08d945021f71a0500d1940958770802
|
|
7
|
+
data.tar.gz: 689c54a748b37a357d7b8b3bc7d15816ebe313eca02e1c479a001aca06e2c5cc79822e32323cd99f6af0e7755cad1cb19cbdfd0b863d9ce084616370b0cd5a93
|
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Robert Gersak / Neyho
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
# EYWA Client for Ruby
|
|
2
|
+
|
|
3
|
+
[](https://badge.fury.io/rb/eywa-client)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
|
|
6
|
+
EYWA client library for Ruby providing JSON-RPC communication, GraphQL queries, and task management for EYWA robots.
|
|
7
|
+
|
|
8
|
+
## Installation
|
|
9
|
+
|
|
10
|
+
Add this line to your application's Gemfile:
|
|
11
|
+
|
|
12
|
+
```ruby
|
|
13
|
+
gem 'eywa-client'
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
And then execute:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
bundle install
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Or install it yourself as:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
gem install eywa-client
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Quick Start
|
|
29
|
+
|
|
30
|
+
```ruby
|
|
31
|
+
require 'eywa'
|
|
32
|
+
|
|
33
|
+
# Initialize the client
|
|
34
|
+
open_pipe
|
|
35
|
+
|
|
36
|
+
# Log messages
|
|
37
|
+
info("Robot started")
|
|
38
|
+
|
|
39
|
+
# Execute GraphQL queries
|
|
40
|
+
result_thread = graphql('
|
|
41
|
+
{
|
|
42
|
+
searchUser(_limit: 10) {
|
|
43
|
+
euuid
|
|
44
|
+
name
|
|
45
|
+
type
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
')
|
|
49
|
+
|
|
50
|
+
result = result_thread.value
|
|
51
|
+
info("Users found", result)
|
|
52
|
+
|
|
53
|
+
# Update task status
|
|
54
|
+
update_task(PROCESSING)
|
|
55
|
+
|
|
56
|
+
# Complete the task
|
|
57
|
+
close_task(SUCCESS)
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Features
|
|
61
|
+
|
|
62
|
+
- 🚀 **Thread-Based Async** - Ruby native threading for async operations
|
|
63
|
+
- 📊 **GraphQL Integration** - Execute queries and mutations against EYWA datasets
|
|
64
|
+
- 📝 **Comprehensive Logging** - Multiple log levels with metadata support
|
|
65
|
+
- 🔄 **Task Management** - Update status, report progress, handle task lifecycle
|
|
66
|
+
- 🎯 **Thread-Safe** - Mutex protection for concurrent operations
|
|
67
|
+
- 💎 **Ruby Idioms** - Keyword arguments and blocks for handlers
|
|
68
|
+
|
|
69
|
+
## API Reference
|
|
70
|
+
|
|
71
|
+
### Initialization
|
|
72
|
+
|
|
73
|
+
#### `open_pipe`
|
|
74
|
+
Initialize stdin/stdout communication with EYWA runtime. Must be called before using other functions.
|
|
75
|
+
|
|
76
|
+
```ruby
|
|
77
|
+
open_pipe
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Logging Functions
|
|
81
|
+
|
|
82
|
+
#### `log(event: "INFO", message:, data: nil, duration: nil, coordinates: nil, time: Time.now)`
|
|
83
|
+
Log a message with full control over all parameters.
|
|
84
|
+
|
|
85
|
+
```ruby
|
|
86
|
+
log(
|
|
87
|
+
event: "INFO",
|
|
88
|
+
message: "Processing item",
|
|
89
|
+
data: { item_id: 123 },
|
|
90
|
+
duration: 1500,
|
|
91
|
+
coordinates: { x: 10, y: 20 }
|
|
92
|
+
)
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
#### `info()`, `error()`, `warn()`, `debug()`, `trace()`, `exception()`
|
|
96
|
+
Convenience methods for different log levels.
|
|
97
|
+
|
|
98
|
+
```ruby
|
|
99
|
+
info("User logged in", { user_id: "abc123" })
|
|
100
|
+
error("Failed to process", { error: e.message })
|
|
101
|
+
exception("Unhandled error", { stack: e.backtrace[0..5] })
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Task Management
|
|
105
|
+
|
|
106
|
+
#### `get_task`
|
|
107
|
+
Get current task information. Returns a Thread.
|
|
108
|
+
|
|
109
|
+
```ruby
|
|
110
|
+
task_thread = get_task
|
|
111
|
+
begin
|
|
112
|
+
task = task_thread.value
|
|
113
|
+
info("Processing task", { task_id: task["euuid"] })
|
|
114
|
+
rescue => e
|
|
115
|
+
warn("Could not get task", { error: e.message })
|
|
116
|
+
end
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
#### `update_task(status = PROCESSING)`
|
|
120
|
+
Update the current task status.
|
|
121
|
+
|
|
122
|
+
```ruby
|
|
123
|
+
update_task(PROCESSING)
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
#### `close_task(status = SUCCESS)`
|
|
127
|
+
Close the task with a final status and exit the process.
|
|
128
|
+
|
|
129
|
+
```ruby
|
|
130
|
+
begin
|
|
131
|
+
# Do work...
|
|
132
|
+
close_task(SUCCESS)
|
|
133
|
+
rescue => e
|
|
134
|
+
error("Task failed", { error: e.message })
|
|
135
|
+
close_task(ERROR)
|
|
136
|
+
end
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
#### `return_task`
|
|
140
|
+
Return control to EYWA without closing the task.
|
|
141
|
+
|
|
142
|
+
```ruby
|
|
143
|
+
return_task
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### Reporting
|
|
147
|
+
|
|
148
|
+
#### `report(message, data = nil, image = nil)`
|
|
149
|
+
Send a task report with optional data and image.
|
|
150
|
+
|
|
151
|
+
```ruby
|
|
152
|
+
report("Analysis complete", {
|
|
153
|
+
accuracy: 0.95,
|
|
154
|
+
processed: 1000
|
|
155
|
+
}, chart_image_base64)
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### GraphQL
|
|
159
|
+
|
|
160
|
+
#### `graphql(query, variables = nil)`
|
|
161
|
+
Execute a GraphQL query against the EYWA server. Returns a Thread.
|
|
162
|
+
|
|
163
|
+
```ruby
|
|
164
|
+
# Simple query
|
|
165
|
+
thread = graphql('{ searchUser { name email } }')
|
|
166
|
+
result = thread.value
|
|
167
|
+
|
|
168
|
+
# Query with variables
|
|
169
|
+
thread = graphql('
|
|
170
|
+
mutation CreateUser($input: UserInput!) {
|
|
171
|
+
syncUser(data: $input) {
|
|
172
|
+
euuid
|
|
173
|
+
name
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
', {
|
|
177
|
+
input: {
|
|
178
|
+
name: "John Doe",
|
|
179
|
+
active: true
|
|
180
|
+
}
|
|
181
|
+
})
|
|
182
|
+
|
|
183
|
+
begin
|
|
184
|
+
result = thread.value
|
|
185
|
+
info("User created", result)
|
|
186
|
+
rescue => e
|
|
187
|
+
error("Creation failed", { error: e.message })
|
|
188
|
+
end
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
### JSON-RPC
|
|
192
|
+
|
|
193
|
+
#### `send_request(data)`
|
|
194
|
+
Send a JSON-RPC request and get a Thread for the response.
|
|
195
|
+
|
|
196
|
+
```ruby
|
|
197
|
+
thread = send_request({
|
|
198
|
+
"method" => "custom.method",
|
|
199
|
+
"params" => { "foo" => "bar" }
|
|
200
|
+
})
|
|
201
|
+
|
|
202
|
+
begin
|
|
203
|
+
result = thread.value
|
|
204
|
+
info("Response received", result)
|
|
205
|
+
rescue => e
|
|
206
|
+
error("Request failed", { error: e.message })
|
|
207
|
+
end
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
#### `send_notification(data)`
|
|
211
|
+
Send a JSON-RPC notification without expecting a response.
|
|
212
|
+
|
|
213
|
+
```ruby
|
|
214
|
+
send_notification({
|
|
215
|
+
"method" => "custom.event",
|
|
216
|
+
"params" => { "status" => "ready" }
|
|
217
|
+
})
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
#### `register_handler(method, &handler)`
|
|
221
|
+
Register a handler for incoming JSON-RPC method calls.
|
|
222
|
+
|
|
223
|
+
```ruby
|
|
224
|
+
register_handler("custom.ping") do |request|
|
|
225
|
+
info("Received ping", request["params"])
|
|
226
|
+
send_notification({
|
|
227
|
+
"method" => "custom.pong",
|
|
228
|
+
"params" => { "timestamp" => Time.now.to_i }
|
|
229
|
+
})
|
|
230
|
+
end
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
## Module Structure
|
|
234
|
+
|
|
235
|
+
The library uses a modular structure with a global client for ease of use:
|
|
236
|
+
|
|
237
|
+
```ruby
|
|
238
|
+
# Direct usage (recommended)
|
|
239
|
+
info("Hello")
|
|
240
|
+
|
|
241
|
+
# Or use the client instance
|
|
242
|
+
Eywa.client.info("Hello")
|
|
243
|
+
|
|
244
|
+
# Access constants
|
|
245
|
+
SUCCESS # => "SUCCESS"
|
|
246
|
+
ERROR # => "ERROR"
|
|
247
|
+
PROCESSING # => "PROCESSING"
|
|
248
|
+
EXCEPTION # => "EXCEPTION"
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
## Complete Example
|
|
252
|
+
|
|
253
|
+
```ruby
|
|
254
|
+
#!/usr/bin/env ruby
|
|
255
|
+
|
|
256
|
+
require 'eywa'
|
|
257
|
+
|
|
258
|
+
def process_data
|
|
259
|
+
# Get task
|
|
260
|
+
task_thread = get_task
|
|
261
|
+
task = task_thread.value
|
|
262
|
+
|
|
263
|
+
info("Starting task", {
|
|
264
|
+
task_id: task["euuid"],
|
|
265
|
+
message: task["message"]
|
|
266
|
+
})
|
|
267
|
+
|
|
268
|
+
# Update status
|
|
269
|
+
update_task(PROCESSING)
|
|
270
|
+
|
|
271
|
+
# Query data
|
|
272
|
+
result_thread = graphql('
|
|
273
|
+
query GetActiveUsers {
|
|
274
|
+
searchUser(_where: {active: {_eq: true}}) {
|
|
275
|
+
euuid
|
|
276
|
+
name
|
|
277
|
+
email
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
')
|
|
281
|
+
|
|
282
|
+
result = result_thread.value
|
|
283
|
+
users = result.dig("data", "searchUser") || []
|
|
284
|
+
|
|
285
|
+
info("Found users", { count: users.length })
|
|
286
|
+
|
|
287
|
+
# Process users
|
|
288
|
+
users.each do |user|
|
|
289
|
+
debug("Processing user", { user_id: user["euuid"] })
|
|
290
|
+
# ... do something
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
# Report results
|
|
294
|
+
report("Found active users", {
|
|
295
|
+
count: users.length,
|
|
296
|
+
user_names: users.map { |u| u["name"] }
|
|
297
|
+
})
|
|
298
|
+
|
|
299
|
+
info("Task completed")
|
|
300
|
+
rescue => e
|
|
301
|
+
error("Task processing failed", {
|
|
302
|
+
error: e.message,
|
|
303
|
+
backtrace: e.backtrace[0..5]
|
|
304
|
+
})
|
|
305
|
+
raise
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
def main
|
|
309
|
+
# Initialize
|
|
310
|
+
open_pipe
|
|
311
|
+
sleep(0.1)
|
|
312
|
+
|
|
313
|
+
info("Robot started")
|
|
314
|
+
|
|
315
|
+
begin
|
|
316
|
+
process_data
|
|
317
|
+
close_task(SUCCESS)
|
|
318
|
+
rescue => e
|
|
319
|
+
error("Task failed", { error: e.message })
|
|
320
|
+
close_task(ERROR)
|
|
321
|
+
end
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
main
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
## Error Handling
|
|
328
|
+
|
|
329
|
+
Threads return exceptions that can be caught:
|
|
330
|
+
|
|
331
|
+
```ruby
|
|
332
|
+
thread = graphql("{ invalid }")
|
|
333
|
+
begin
|
|
334
|
+
result = thread.value
|
|
335
|
+
rescue => e
|
|
336
|
+
error("GraphQL failed", { error: e.message })
|
|
337
|
+
end
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
## Thread Safety
|
|
341
|
+
|
|
342
|
+
All operations are thread-safe:
|
|
343
|
+
- Mutex protection for callbacks and handlers
|
|
344
|
+
- Thread-safe Queue for request/response correlation
|
|
345
|
+
- Safe concurrent access to shared state
|
|
346
|
+
|
|
347
|
+
## Testing
|
|
348
|
+
|
|
349
|
+
Test your robot locally using the EYWA CLI:
|
|
350
|
+
|
|
351
|
+
```bash
|
|
352
|
+
eywa run -c 'ruby my_robot.rb'
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
## Requirements
|
|
356
|
+
|
|
357
|
+
- Ruby 2.5+
|
|
358
|
+
- No external gem dependencies (uses only standard library)
|
|
359
|
+
|
|
360
|
+
## License
|
|
361
|
+
|
|
362
|
+
MIT
|
|
363
|
+
|
|
364
|
+
## Contributing
|
|
365
|
+
|
|
366
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
367
|
+
|
|
368
|
+
## Support
|
|
369
|
+
|
|
370
|
+
For issues and questions, please visit the [EYWA repository](https://github.com/neyho/eywa).
|
data/lib/eywa.rb
ADDED
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
require 'securerandom'
|
|
2
|
+
require 'json'
|
|
3
|
+
require 'time'
|
|
4
|
+
|
|
5
|
+
module Eywa
|
|
6
|
+
# Version
|
|
7
|
+
VERSION = "0.3.0"
|
|
8
|
+
|
|
9
|
+
# Task status constants
|
|
10
|
+
SUCCESS = 'SUCCESS'
|
|
11
|
+
ERROR = 'ERROR'
|
|
12
|
+
PROCESSING = 'PROCESSING'
|
|
13
|
+
EXCEPTION = 'EXCEPTION'
|
|
14
|
+
|
|
15
|
+
class Client
|
|
16
|
+
def initialize
|
|
17
|
+
@rpc_callbacks = {}
|
|
18
|
+
@handlers = {}
|
|
19
|
+
@mutex = Mutex.new
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def open_pipe
|
|
23
|
+
Thread.new do
|
|
24
|
+
while (line = STDIN.gets)
|
|
25
|
+
begin
|
|
26
|
+
json = JSON.parse(line)
|
|
27
|
+
handle_data(json)
|
|
28
|
+
rescue JSON::ParserError => e
|
|
29
|
+
STDERR.puts("Failed to parse JSON: #{e.message}")
|
|
30
|
+
rescue => e
|
|
31
|
+
STDERR.puts("Error handling data: #{e.message}")
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def send_request(data)
|
|
38
|
+
id = SecureRandom.uuid
|
|
39
|
+
data['jsonrpc'] = '2.0'
|
|
40
|
+
data['id'] = id
|
|
41
|
+
|
|
42
|
+
# Create a queue for this request
|
|
43
|
+
queue = Queue.new
|
|
44
|
+
|
|
45
|
+
@mutex.synchronize do
|
|
46
|
+
@rpc_callbacks[id] = queue
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Send the request
|
|
50
|
+
STDOUT.puts(data.to_json)
|
|
51
|
+
STDOUT.flush
|
|
52
|
+
|
|
53
|
+
# Wait for response in a thread
|
|
54
|
+
Thread.new do
|
|
55
|
+
response = queue.pop
|
|
56
|
+
@mutex.synchronize do
|
|
57
|
+
@rpc_callbacks.delete(id)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
if response['error']
|
|
61
|
+
raise StandardError.new(response['error']['message'] || response['error'].to_s)
|
|
62
|
+
else
|
|
63
|
+
response['result']
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def send_notification(data)
|
|
69
|
+
data['jsonrpc'] = '2.0'
|
|
70
|
+
STDOUT.puts(data.to_json)
|
|
71
|
+
STDOUT.flush
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def register_handler(method, &handler)
|
|
75
|
+
@mutex.synchronize do
|
|
76
|
+
@handlers[method] = handler
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def log(event: 'INFO', message:, data: nil, duration: nil, coordinates: nil, time: Time.now)
|
|
81
|
+
send_notification(
|
|
82
|
+
'method' => 'task.log',
|
|
83
|
+
'params' => {
|
|
84
|
+
'time' => time.iso8601,
|
|
85
|
+
'event' => event,
|
|
86
|
+
'message' => message,
|
|
87
|
+
'data' => data,
|
|
88
|
+
'coordinates' => coordinates,
|
|
89
|
+
'duration' => duration
|
|
90
|
+
}
|
|
91
|
+
)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def info(message, data = nil)
|
|
95
|
+
log(event: 'INFO', message: message, data: data)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def error(message, data = nil)
|
|
99
|
+
log(event: 'ERROR', message: message, data: data)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def warn(message, data = nil)
|
|
103
|
+
log(event: 'WARN', message: message, data: data)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def debug(message, data = nil)
|
|
107
|
+
log(event: 'DEBUG', message: message, data: data)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def trace(message, data = nil)
|
|
111
|
+
log(event: 'TRACE', message: message, data: data)
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def exception(message, data = nil)
|
|
115
|
+
log(event: 'EXCEPTION', message: message, data: data)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def report(message, data = nil, image = nil)
|
|
119
|
+
send_notification(
|
|
120
|
+
'method' => 'task.report',
|
|
121
|
+
'params' => {
|
|
122
|
+
'message' => message,
|
|
123
|
+
'data' => data,
|
|
124
|
+
'image' => image
|
|
125
|
+
}
|
|
126
|
+
)
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def close_task(status = SUCCESS)
|
|
130
|
+
send_notification(
|
|
131
|
+
'method' => 'task.close',
|
|
132
|
+
'params' => {
|
|
133
|
+
'status' => status
|
|
134
|
+
}
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
exit(status == SUCCESS ? 0 : 1)
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def update_task(status = PROCESSING)
|
|
141
|
+
send_notification(
|
|
142
|
+
'method' => 'task.update',
|
|
143
|
+
'params' => {
|
|
144
|
+
'status' => status
|
|
145
|
+
}
|
|
146
|
+
)
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def get_task
|
|
150
|
+
send_request('method' => 'task.get')
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def return_task
|
|
154
|
+
send_notification('method' => 'task.return')
|
|
155
|
+
exit(0)
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def graphql(query, variables = nil)
|
|
159
|
+
send_request(
|
|
160
|
+
'method' => 'eywa.datasets.graphql',
|
|
161
|
+
'params' => {
|
|
162
|
+
'query' => query,
|
|
163
|
+
'variables' => variables
|
|
164
|
+
}
|
|
165
|
+
)
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
private
|
|
169
|
+
|
|
170
|
+
def handle_data(data)
|
|
171
|
+
method = data['method']
|
|
172
|
+
id = data['id']
|
|
173
|
+
result = data['result']
|
|
174
|
+
error = data['error']
|
|
175
|
+
|
|
176
|
+
if method
|
|
177
|
+
handle_request(data)
|
|
178
|
+
elsif (result || error) && id
|
|
179
|
+
handle_response(data)
|
|
180
|
+
else
|
|
181
|
+
STDERR.puts("Received invalid JSON-RPC:\n#{data}")
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def handle_request(data)
|
|
186
|
+
method = data['method']
|
|
187
|
+
|
|
188
|
+
@mutex.synchronize do
|
|
189
|
+
handler = @handlers[method]
|
|
190
|
+
if handler
|
|
191
|
+
handler.call(data)
|
|
192
|
+
else
|
|
193
|
+
STDERR.puts("Method #{method} doesn't have registered handler")
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
def handle_response(data)
|
|
199
|
+
id = data['id']
|
|
200
|
+
|
|
201
|
+
@mutex.synchronize do
|
|
202
|
+
queue = @rpc_callbacks[id]
|
|
203
|
+
if queue
|
|
204
|
+
queue.push(data)
|
|
205
|
+
else
|
|
206
|
+
STDERR.puts("RPC callback not registered for request with id = #{id}")
|
|
207
|
+
end
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
# Convenience module for global access
|
|
213
|
+
module GlobalClient
|
|
214
|
+
extend self
|
|
215
|
+
|
|
216
|
+
def client
|
|
217
|
+
@client ||= Client.new
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
# Delegate all methods to the client instance
|
|
221
|
+
def method_missing(method_name, *args, **kwargs, &block)
|
|
222
|
+
if client.respond_to?(method_name)
|
|
223
|
+
client.send(method_name, *args, **kwargs, &block)
|
|
224
|
+
else
|
|
225
|
+
super
|
|
226
|
+
end
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
def respond_to_missing?(method_name, include_private = false)
|
|
230
|
+
client.respond_to?(method_name, include_private) || super
|
|
231
|
+
end
|
|
232
|
+
end
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
# For backward compatibility and ease of use
|
|
236
|
+
include Eywa::GlobalClient
|
|
237
|
+
|
|
238
|
+
# Export constants for easy access
|
|
239
|
+
SUCCESS = Eywa::SUCCESS
|
|
240
|
+
ERROR = Eywa::ERROR
|
|
241
|
+
PROCESSING = Eywa::PROCESSING
|
|
242
|
+
EXCEPTION = Eywa::EXCEPTION
|
metadata
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: eywa-client
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.3.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Robert Gersak
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2025-06-06 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: json
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - "~>"
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '2.0'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - "~>"
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '2.0'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: rake
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - "~>"
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '13.0'
|
|
34
|
+
type: :development
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - "~>"
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '13.0'
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: minitest
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - "~>"
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '5.0'
|
|
48
|
+
type: :development
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - "~>"
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '5.0'
|
|
55
|
+
description: A Ruby gem that provides a client for EYWA server with JSON-RPC communication,
|
|
56
|
+
GraphQL support, and task management.
|
|
57
|
+
email:
|
|
58
|
+
- robi@neyho.com
|
|
59
|
+
executables: []
|
|
60
|
+
extensions: []
|
|
61
|
+
extra_rdoc_files: []
|
|
62
|
+
files:
|
|
63
|
+
- LICENSE
|
|
64
|
+
- README.md
|
|
65
|
+
- lib/eywa.rb
|
|
66
|
+
homepage: https://github.com/neyho/eywa
|
|
67
|
+
licenses:
|
|
68
|
+
- MIT
|
|
69
|
+
metadata:
|
|
70
|
+
homepage_uri: https://github.com/neyho/eywa
|
|
71
|
+
source_code_uri: https://github.com/neyho/eywa
|
|
72
|
+
changelog_uri: https://github.com/neyho/eywa/blob/main/CHANGELOG.md
|
|
73
|
+
post_install_message:
|
|
74
|
+
rdoc_options: []
|
|
75
|
+
require_paths:
|
|
76
|
+
- lib
|
|
77
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
78
|
+
requirements:
|
|
79
|
+
- - ">="
|
|
80
|
+
- !ruby/object:Gem::Version
|
|
81
|
+
version: 2.5.0
|
|
82
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
83
|
+
requirements:
|
|
84
|
+
- - ">="
|
|
85
|
+
- !ruby/object:Gem::Version
|
|
86
|
+
version: '0'
|
|
87
|
+
requirements: []
|
|
88
|
+
rubygems_version: 3.0.3.1
|
|
89
|
+
signing_key:
|
|
90
|
+
specification_version: 4
|
|
91
|
+
summary: EYWA client for asynchronous communication with EYWA server
|
|
92
|
+
test_files: []
|