Class: BazaRb
- Inherits:
-
Object
- Object
- BazaRb
- Includes:
- Compress
- Defined in:
- lib/baza-rb.rb,
lib/baza-rb/version.rb,
lib/baza-rb/compress.rb
Overview
Compression helpers for BazaRb.
Provides gzip compression and decompression for request bodies and response data.
- Author
Yegor Bugayenko (yegor256@gmail.com)
- Copyright
Copyright (c) 2024-2026 Yegor Bugayenko
- License
MIT
Defined Under Namespace
Modules: Compress Classes: BadCompression, ConnectionFailed, Fake, ServerFailure, TimedOut
Constant Summary collapse
- DEFAULT_CHUNK_SIZE =
1_000_000- VERSION =
'0.15.0'
Constants included from Compress
Instance Method Summary collapse
-
#balance ⇒ Float
Get current balance of the authenticated user.
-
#csrf ⇒ String
Get CSRF token from the server for authenticated requests.
-
#durable_find(pname, file) ⇒ Integer?
Find a durable by job name and file name.
-
#durable_load(id, file) ⇒ Object
Load a single durable from server to local file.
-
#durable_lock(id, owner) ⇒ Object
Lock a single durable.
-
#durable_place(pname, file) ⇒ Integer
Place a single durable file on the server.
-
#durable_save(id, file, chunk_size: DEFAULT_CHUNK_SIZE) ⇒ Object
Save a single durable from local file to server.
-
#durable_unlock(id, owner) ⇒ Object
Unlock a single durable.
-
#enter(pname, badge, why, job) { ... } ⇒ String
Enter a valve to cache or retrieve a computation result.
-
#exit_code(id) ⇒ Integer
Read and return the exit code of the job.
-
#fee(tab, amount, summary, job) ⇒ Integer
Pay a fee associated with a job.
-
#finished?(id) ⇒ Boolean
Check if the job with this ID is finished already.
-
#initialize(host, port, token, ssl: true, timeout: 30, retries: 5, pause: 1, loog: Loog::NULL, compress: true) ⇒ BazaRb
constructor
Initialize a new Zerocracy API client.
-
#lock(pname, owner) ⇒ Object
Lock the name.
-
#name_exists?(pname) ⇒ Boolean
Check whether the name of the job exists on the server.
-
#pull(id) ⇒ String
Pull factbase from the server for a specific job.
-
#push(pname, data, meta, chunk_size: DEFAULT_CHUNK_SIZE) ⇒ Object
Push factbase to the server to create a new job.
-
#recent(name) ⇒ Integer
Get the ID of the job by the name.
-
#stdout(id) ⇒ String
Read and return the stdout of the job.
-
#transfer(recipient, amount, summary, job: nil, badge: nil) ⇒ Integer
Transfer funds to another user.
-
#unlock(pname, owner) ⇒ Object
Unlock the name.
-
#verified(id) ⇒ String
Read and return the verification verdict of the job.
-
#whoami ⇒ String
Get GitHub login name of the logged in user.
Methods included from Compress
Constructor Details
#initialize(host, port, token, ssl: true, timeout: 30, retries: 5, pause: 1, loog: Loog::NULL, compress: true) ⇒ BazaRb
Initialize a new Zerocracy API client.
66 67 68 69 70 71 72 73 74 75 76 77 |
# File 'lib/baza-rb.rb', line 66 def initialize(host, port, token, ssl: true, timeout: 30, retries: 5, pause: 1, loog: Loog::NULL, compress: true) @host = host @port = port @ssl = ssl @token = token @timeout = timeout @loog = loog @retries = retries @pause = ENV.fetch('BAZA_BACKOFF', pause).to_i @compress = compress @mutex = Mutex.new end |
Instance Method Details
#balance ⇒ Float
Get current balance of the authenticated user.
96 97 98 99 100 101 102 103 |
# File 'lib/baza-rb.rb', line 96 def balance z = nil elapsed(@loog, level: Logger::INFO) do z = get(home.append('account').append('balance')).body.to_f throw(:"The balance is Ƶ#{z}, at #{@host}") end z end |
#csrf ⇒ String
Get CSRF token from the server for authenticated requests.
The CSRF token is required for POST requests to prevent cross-site request forgery attacks.
546 547 548 549 550 551 552 553 |
# File 'lib/baza-rb.rb', line 546 def csrf token = nil elapsed(@loog, level: Logger::INFO) do token = get(home.append('csrf')).body throw(:"CSRF token retrieved (#{token.length} chars)") end token end |
#durable_find(pname, file) ⇒ Integer?
Find a durable by job name and file name.
413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 |
# File 'lib/baza-rb.rb', line 413 def durable_find(pname, file) raise(RuntimeError, 'The "pname" is nil') if pname.nil? raise(RuntimeError, 'The "pname" may not be empty') if pname.empty? raise(RuntimeError, "The name #{pname.inspect} is not valid") unless pname.match?(/\A[a-z0-9-]+\z/) raise(RuntimeError, "The name #{pname.inspect} is too long") if pname.length > 32 raise(RuntimeError, 'The "file" is nil') if file.nil? raise(RuntimeError, 'The "file" may not be empty') if file.empty? id = nil elapsed(@loog, level: Logger::INFO) do ret = get(home.append('durable-find').add(file:, pname:), [200, 404]) if ret.code == 200 id = ret.body.to_i throw(:"Found durable ##{id} for job \"#{pname}\" file \"#{file}\" at #{@host}") else throw(:"Durable not found for job \"#{pname}\" file \"#{file}\" at #{@host}") end end id end |
#durable_load(id, file) ⇒ Object
Load a single durable from server to local file.
363 364 365 366 367 368 369 370 371 372 |
# File 'lib/baza-rb.rb', line 363 def durable_load(id, file) raise(RuntimeError, 'The ID of the durable is nil') if id.nil? raise(RuntimeError, 'The ID of the durable must be an Integer') unless id.is_a?(Integer) raise(RuntimeError, 'The ID of the durable must be a positive integer') unless id.positive? raise(RuntimeError, 'The "file" of the durable is nil') if file.nil? elapsed(@loog, level: Logger::INFO) do download(home.append('durables').append(id), file) throw(:"Durable ##{id} loaded #{File.size(file)} bytes from #{@host}") end end |
#durable_lock(id, owner) ⇒ Object
Lock a single durable.
379 380 381 382 383 384 385 386 387 388 389 |
# File 'lib/baza-rb.rb', line 379 def durable_lock(id, owner) raise(RuntimeError, 'The ID of the durable is nil') if id.nil? raise(RuntimeError, 'The ID of the durable must be an Integer') unless id.is_a?(Integer) raise(RuntimeError, 'The ID of the durable must be a positive integer') unless id.positive? raise(RuntimeError, 'The "owner" of the lock is nil') if owner.nil? raise(RuntimeError, 'The "owner" of the lock may not be empty') if owner.empty? elapsed(@loog, level: Logger::INFO) do post(home.append('durables').append(id).append('lock'), { 'owner' => owner }) throw(:"Durable ##{id} locked at #{@host}") end end |
#durable_place(pname, file) ⇒ Integer
Place a single durable file on the server.
The file provided will only be uploaded to the server if the durable is currently absent. If the durable is present, the file will be ignored. It is expected to use only small placeholder files, not real data.
312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 |
# File 'lib/baza-rb.rb', line 312 def durable_place(pname, file) raise(RuntimeError, 'The "pname" of the durable is nil') if pname.nil? raise(RuntimeError, 'The "pname" of the durable may not be empty') if pname.empty? raise(RuntimeError, "The name #{pname.inspect} is not valid") unless pname.match?(/\A[a-z0-9-]+\z/) raise(RuntimeError, "The name #{pname.inspect} is too long") if pname.length > 32 raise(RuntimeError, 'The "file" of the durable is nil') if file.nil? raise(RuntimeError, "The file '#{file}' is absent") unless File.exist?(file) if File.size(file) > 1024 raise( RuntimeError, "The file '#{file}' is too big (#{File.size(file)} bytes) for durable_place(), use durable_save() instead" ) end id = nil elapsed(@loog, level: Logger::INFO) do id = post( home.append('durable-place'), { 'pname' => pname, 'file' => File.basename(file), 'zip' => File.open(file, 'rb') } ).headers['X-Zerocracy-DurableId'].to_i throw(:"Durable ##{id} (#{file}, #{File.size(file)} bytes) placed for job \"#{pname}\" at #{@host}") end id end |
#durable_save(id, file, chunk_size: DEFAULT_CHUNK_SIZE) ⇒ Object
Save a single durable from local file to server.
346 347 348 349 350 351 352 353 354 355 356 |
# File 'lib/baza-rb.rb', line 346 def durable_save(id, file, chunk_size: DEFAULT_CHUNK_SIZE) raise(RuntimeError, 'The ID of the durable is nil') if id.nil? raise(RuntimeError, 'The ID of the durable must be an Integer') unless id.is_a?(Integer) raise(RuntimeError, 'The ID of the durable must be a positive integer') unless id.positive? raise(RuntimeError, 'The "file" of the durable is nil') if file.nil? raise(RuntimeError, "The file '#{file}' is absent") unless File.exist?(file) elapsed(@loog, level: Logger::INFO) do upload(home.append('durables').append(id), file, chunk_size:) throw(:"Durable ##{id} saved #{File.size(file)} bytes to #{@host}") end end |
#durable_unlock(id, owner) ⇒ Object
Unlock a single durable.
396 397 398 399 400 401 402 403 404 405 406 |
# File 'lib/baza-rb.rb', line 396 def durable_unlock(id, owner) raise(RuntimeError, 'The ID of the durable is nil') if id.nil? raise(RuntimeError, 'The ID of the durable must be an Integer') unless id.is_a?(Integer) raise(RuntimeError, 'The ID of the durable must be a positive integer') unless id.positive? raise(RuntimeError, 'The "owner" of the lock is nil') if owner.nil? raise(RuntimeError, 'The "owner" of the lock may not be empty') if owner.empty? elapsed(@loog, level: Logger::INFO) do post(home.append('durables').append(id).append('unlock'), { 'owner' => owner }) throw(:"Durable ##{id} unlocked at #{@host}") end end |
#enter(pname, badge, why, job) { ... } ⇒ String
Enter a valve to cache or retrieve a computation result.
Valves prevent duplicate computations by caching results. If a result for the given badge already exists, it's returned. Otherwise, the block is executed and its result is cached.
519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 |
# File 'lib/baza-rb.rb', line 519 def enter(pname, badge, why, job) raise(RuntimeError, 'The "pname" is nil') if pname.nil? raise(RuntimeError, 'The "pname" may not be empty') if pname.empty? raise(RuntimeError, 'The "badge" is nil') if badge.nil? raise(RuntimeError, 'The "badge" may not be empty') if badge.empty? raise(RuntimeError, 'The "why" is nil') if why.nil? raise(RuntimeError, 'The "why" may not be empty') if why.empty? raise(RuntimeError, 'The "job" must be an Integer') unless job.nil? || job.is_a?(Integer) raise(RuntimeError, 'The "job" must be positive') unless job.nil? || job.positive? elapsed(@loog, good: "Entered valve #{badge} to #{pname}") do ret = get(home.append('result').add(badge:), [200, 204]) return ret.body if ret.code == 200 r = yield uri = home.append('valves') uri = uri.add(job:) unless job.nil? post(uri, { 'badge' => badge, 'pname' => pname, 'result' => r.to_s, 'why' => why }) r end end |
#exit_code(id) ⇒ Integer
Read and return the exit code of the job.
197 198 199 200 201 202 203 204 205 206 207 |
# File 'lib/baza-rb.rb', line 197 def exit_code(id) raise(RuntimeError, 'The ID of the job is nil') if id.nil? raise(RuntimeError, 'The ID of the job must be an Integer') unless id.is_a?(Integer) raise(RuntimeError, 'The ID of the job must be a positive integer') unless id.positive? code = 0 elapsed(@loog, level: Logger::INFO) do code = get(home.append('exit').append("#{id}.txt")).body.to_i throw(:"The exit code of the job ##{id} is #{code}") end code end |
#fee(tab, amount, summary, job) ⇒ Integer
Pay a fee associated with a job.
477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 |
# File 'lib/baza-rb.rb', line 477 def fee(tab, amount, summary, job) raise(RuntimeError, 'The "tab" is nil') if tab.nil? raise(RuntimeError, 'The "amount" is nil') if amount.nil? unless amount.is_a?(Float) || amount.is_a?(BigDecimal) raise(RuntimeError, 'The "amount" must be Float or BigDecimal') end raise(RuntimeError, 'The "amount" must be positive') unless amount.positive? raise(RuntimeError, 'The "job" is nil') if job.nil? raise(RuntimeError, 'The "job" must be Integer') unless job.is_a?(Integer) raise(RuntimeError, 'The "job" must be positive') unless job.positive? raise(RuntimeError, 'The "summary" is nil') if summary.nil? raise(RuntimeError, "The summary #{summary.inspect} is empty") if summary.empty? amt = amount.is_a?(BigDecimal) ? amount.truncate(6).to_s('F') : format('%0.6f', amount) id = nil elapsed(@loog, level: Logger::INFO) do id = post( home.append('account').append('fee'), { 'amount' => amt, 'job' => job.to_s, 'summary' => summary, 'tab' => tab } ).headers['X-Zerocracy-ReceiptId'].to_i throw(:"Fee Ƶ#{amt} paid at #{@host}") end id end |
#finished?(id) ⇒ Boolean
Check if the job with this ID is finished already.
162 163 164 165 166 167 168 169 170 171 172 173 |
# File 'lib/baza-rb.rb', line 162 def finished?(id) raise(RuntimeError, 'The ID of the job is nil') if id.nil? raise(RuntimeError, 'The ID of the job must be an Integer') unless id.is_a?(Integer) raise(RuntimeError, 'The ID of the job must be a positive integer') unless id.positive? fin = false elapsed(@loog, level: Logger::INFO) do ret = get(home.append('finished').append(id)) fin = ret.body == 'yes' throw(:"The job ##{id} is #{'not yet ' unless fin}finished at #{@host}#{" (#{ret.body.inspect})" unless fin}") end fin end |
#lock(pname, owner) ⇒ Object
Lock the name.
232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 |
# File 'lib/baza-rb.rb', line 232 def lock(pname, owner) raise(RuntimeError, 'The "pname" of the product is nil') if pname.nil? raise(RuntimeError, 'The "pname" of the product may not be empty') if pname.empty? raise(RuntimeError, "The name #{pname.inspect} is not valid") unless pname.match?(/\A[a-z0-9-]+\z/) raise(RuntimeError, "The name #{pname.inspect} is too long") if pname.length > 32 raise(RuntimeError, 'The "owner" of the lock is nil') if owner.nil? raise(RuntimeError, 'The "owner" of the lock may not be empty') if owner.empty? elapsed(@loog, level: Logger::INFO) do throw(:"Product name #{pname.inspect} locked at #{@host}") if post( home.append('lock').append(pname), { 'owner' => owner }, [302, 409] ).code == 302 raise(RuntimeError, "Failed to lock #{pname.inspect} product at #{@host}, it's already locked") end end |
#name_exists?(pname) ⇒ Boolean
Check whether the name of the job exists on the server.
288 289 290 291 292 293 294 295 296 297 298 299 |
# File 'lib/baza-rb.rb', line 288 def name_exists?(pname) raise(RuntimeError, 'The "pname" of the product is nil') if pname.nil? raise(RuntimeError, 'The "pname" of the product may not be empty') if pname.empty? raise(RuntimeError, "The name #{pname.inspect} is not valid") unless pname.match?(/\A[a-z0-9-]+\z/) raise(RuntimeError, "The name #{pname.inspect} is too long") if pname.length > 32 exists = false elapsed(@loog, level: Logger::INFO) do exists = get(home.append('exists').append(pname)).body == 'yes' throw(:"The name #{pname.inspect} #{exists ? 'exists' : "doesn't exist"} at #{@host}") end exists end |
#pull(id) ⇒ String
Pull factbase from the server for a specific job.
142 143 144 145 146 147 148 149 150 151 152 153 154 155 |
# File 'lib/baza-rb.rb', line 142 def pull(id) raise(RuntimeError, 'The ID of the job is nil') if id.nil? raise(RuntimeError, 'The ID of the job must be an Integer') unless id.is_a?(Integer) raise(RuntimeError, 'The ID of the job must be a positive integer') unless id.positive? data = '' elapsed(@loog, level: Logger::INFO) do Tempfile.open do |file| download(home.append('pull').append("#{id}.fb"), file.path) data = File.binread(file) throw(:"Pulled #{data.bytesize} bytes of job ##{id} factbase at #{@host}") end end data end |
#push(pname, data, meta, chunk_size: DEFAULT_CHUNK_SIZE) ⇒ Object
Push factbase to the server to create a new job.
112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 |
# File 'lib/baza-rb.rb', line 112 def push(pname, data, , chunk_size: DEFAULT_CHUNK_SIZE) raise(RuntimeError, 'The "name" of the job is nil') if pname.nil? raise(RuntimeError, 'The "name" of the job may not be empty') if pname.empty? raise(RuntimeError, "The name #{pname.inspect} is not valid") unless pname.match?(/\A[a-z0-9-]+\z/) raise(RuntimeError, "The name #{pname.inspect} is too long") if pname.length > 32 raise(RuntimeError, 'The "data" of the job is nil') if data.nil? raise(RuntimeError, 'The "data" of the job may not be empty') if data.empty? raise(RuntimeError, 'The "meta" of the job is nil') if .nil? raise(RuntimeError, 'The "meta" of the job must be an Array') unless .is_a?(Array) elapsed(@loog, level: Logger::INFO) do Tempfile.open do |file| File.binwrite(file.path, data) upload( home.append('push').append(pname), file.path, headers.merge( 'X-Zerocracy-Meta' => .map { |v| Base64.strict_encode64(v) }.join(' ') ), chunk_size: ) end throw(:"Pushed #{data.bytesize} bytes to #{@host}") end end |
#recent(name) ⇒ Integer
Get the ID of the job by the name.
271 272 273 274 275 276 277 278 279 280 281 282 |
# File 'lib/baza-rb.rb', line 271 def recent(name) raise(RuntimeError, 'The "name" of the job is nil') if name.nil? raise(RuntimeError, 'The "name" of the job may not be empty') if name.empty? raise(RuntimeError, "The name #{name.inspect} is not valid") unless name.match?(/\A[a-z0-9-]+\z/) raise(RuntimeError, "The name #{name.inspect} is too long") if name.length > 32 job = nil elapsed(@loog, level: Logger::INFO) do job = get(home.append('recent').append("#{name}.txt")).body.to_i throw(:"The recent \"#{name}\" job's ID is ##{job} at #{@host}") end job end |
#stdout(id) ⇒ String
Read and return the stdout of the job.
180 181 182 183 184 185 186 187 188 189 190 |
# File 'lib/baza-rb.rb', line 180 def stdout(id) raise(RuntimeError, 'The ID of the job is nil') if id.nil? raise(RuntimeError, 'The ID of the job must be an Integer') unless id.is_a?(Integer) raise(RuntimeError, 'The ID of the job must be a positive integer') unless id.positive? stdout = '' elapsed(@loog, level: Logger::INFO) do stdout = get(home.append('stdout').append("#{id}.txt")).body throw(:"The stdout of the job ##{id} has #{stdout.split("\n").count} lines") end stdout end |
#transfer(recipient, amount, summary, job: nil, badge: nil) ⇒ Integer
Transfer funds to another user.
443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 |
# File 'lib/baza-rb.rb', line 443 def transfer(recipient, amount, summary, job: nil, badge: nil) raise(RuntimeError, 'The "recipient" is nil') if recipient.nil? raise(RuntimeError, "The recipient #{recipient.inspect} is not valid") unless recipient.match?(/\A[a-zA-Z0-9-]+\z/) raise(RuntimeError, 'The "amount" is nil') if amount.nil? unless amount.is_a?(Float) || amount.is_a?(BigDecimal) raise(RuntimeError, 'The "amount" must be Float or BigDecimal') end raise(RuntimeError, 'The "amount" must be positive') unless amount.positive? raise(RuntimeError, 'The "summary" is nil') if summary.nil? raise(RuntimeError, "The summary #{summary.inspect} is empty") if summary.empty? unless job.nil? raise(RuntimeError, 'The ID must be an Integer') unless job.is_a?(Integer) raise(RuntimeError, 'The ID must be positive') unless job.positive? end amt = amount.is_a?(BigDecimal) ? amount.truncate(6).to_s('F') : format('%0.6f', amount) id = nil body = { 'human' => recipient, 'amount' => amt, 'summary' => summary } body['job'] = job unless job.nil? body['badge'] = badge unless badge.nil? elapsed(@loog, level: Logger::INFO) do id = post(home.append('account').append('transfer'), body).headers['X-Zerocracy-ReceiptId'].to_i throw(:"Transferred Ƶ#{amt} to @#{recipient} at #{@host}") end id end |
#unlock(pname, owner) ⇒ Object
Unlock the name.
253 254 255 256 257 258 259 260 261 262 263 264 |
# File 'lib/baza-rb.rb', line 253 def unlock(pname, owner) raise(RuntimeError, 'The "pname" of the job is nil') if pname.nil? raise(RuntimeError, 'The "pname" of the job may not be empty') if pname.empty? raise(RuntimeError, "The name #{pname.inspect} is not valid") unless pname.match?(/\A[a-z0-9-]+\z/) raise(RuntimeError, "The name #{pname.inspect} is too long") if pname.length > 32 raise(RuntimeError, 'The "owner" of the lock is nil') if owner.nil? raise(RuntimeError, 'The "owner" of the lock may not be empty') if owner.empty? elapsed(@loog, level: Logger::INFO) do post(home.append('unlock').append(pname), { 'owner' => owner }) throw(:"Job name #{pname.inspect} unlocked at #{@host}") end end |
#verified(id) ⇒ String
Read and return the verification verdict of the job.
214 215 216 217 218 219 220 221 222 223 224 |
# File 'lib/baza-rb.rb', line 214 def verified(id) raise(RuntimeError, 'The ID of the job is nil') if id.nil? raise(RuntimeError, 'The ID of the job must be an Integer') unless id.is_a?(Integer) raise(RuntimeError, 'The ID of the job must be a positive integer') unless id.positive? verdict = '' elapsed(@loog, level: Logger::INFO) do verdict = get(home.append('jobs').append(id).append('verified.txt')).body throw(:"The verdict of the job ##{id} is #{verdict.inspect}") end verdict end |
#whoami ⇒ String
Get GitHub login name of the logged in user.
83 84 85 86 87 88 89 90 |
# File 'lib/baza-rb.rb', line 83 def whoami nick = nil elapsed(@loog, level: Logger::INFO) do nick = get(home.append('whoami')).body throw(:"I know that I am @#{nick}, at #{@host}") end nick end |