aromancer 0.0.7 → 0.0.8

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5ebbde2a81b320eab73f2035b03d5166c5bcec1c01eaef7e51e4a010a8ab8251
4
- data.tar.gz: 044c185f6741090deb4e179c8716e35d23e1b6a775c42db683ee079db8cb1419
3
+ metadata.gz: da80d9c4f943470ac226c37651e21eaa5ee12128beb9c00264b43ace9845c61c
4
+ data.tar.gz: c55101847c0d3e6c14d7c821dc9fdf31d4e114d614d97957b7162ab995d5a02e
5
5
  SHA512:
6
- metadata.gz: d992314e5e012369313167b145991a687ec0cfaa2a4e6b8ef445f147333e64e287eb70e9208215fac91b6ef20b04e0b3f740ee20ffd7f3c1b60197ad536dd4ad
7
- data.tar.gz: 9210fd3030350a65b7f56ee68be13296a2707514f9cb1cf378e792b95868ad08c0009ec7c6c7409d6060919ee28911b557b4b4e673b933f47790b50ea2a46b4f
6
+ metadata.gz: 706112cc7fbf39edadef53eb6ac54a73c4e43d17f0f3b288cb9c795ba937bc38ec8427bb74ddcfbd8c84411483c520a8d9ac9b576f4371c1f0c656c2e36adb69
7
+ data.tar.gz: 91aa390ba4cf322eef5e616053cce00ebaba35ef0b9d13e051f2ba71a08bdf7c7ba5a9669d0279f88e7a67ed619a2cddba2b36f5eddc52854f691f2b74458fbc
data/.DS_Store ADDED
Binary file
data/CODE_OF_CONDUCT.md CHANGED
@@ -7,4 +7,4 @@
7
7
  * When interpreting the words and actions of others, participants should always assume good intentions.
8
8
  * Behaviour which can be reasonably considered harassment will not be tolerated.
9
9
 
10
- If you have any concerns about behaviour within this project, please contact us at ["i2097i@hotmail.com"](mailto:"i2097i@hotmail.com").
10
+ If you have any concerns about behavior within this project, please contact us at ["i2097i@hotmail.com"](mailto:"i2097i@hotmail.com").
data/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2026 i2097i
3
+ Copyright (c) 2026 Scot Reichman
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
data/README.md CHANGED
@@ -24,4 +24,4 @@ aromancer
24
24
  [license](http://opensource.org/licenses/MIT)
25
25
  [issues](https://github.com/i2097i/aromancer/issues)
26
26
 
27
- #### aromancer by i2097i is copyright (2026)
27
+ #### aromancer by scot reichman is copyright (2026)
@@ -0,0 +1 @@
1
+ ea42cb760ae909d4de2b750f3ca7f125d58b9e9147886927e8f5b7dd81a80913152d6052c1c517a99a0733e7d3b15de86d3083d1f82d5341e4c8f1e3364222a2
data/lib/aromancer/api.rb CHANGED
@@ -24,23 +24,12 @@ module Aromancer
24
24
  body: response.body.to_s
25
25
  }
26
26
  Aromancer::Storage.set_last_error(le)
27
- Aromancer.print_last_error
28
- signed_out = false
29
- signed_out = response.status == 401
30
- # error_body = nil
31
- # begin
32
- # error_body = JSON.parse(response.body)
33
- # rescue StandardError => e
34
- # end
35
-
36
- # signed_out = signed_out &&
37
- # !error_body.nil? &&
38
- # error_body.keys.include?(:error_message.to_s) &&
39
- # error_body["error_message"] == "not signed in"
40
-
41
- if signed_out
42
- Aromancer::Storage.instance.clear_cache!
43
- Aromancer::Storage.set_signed_out_flag(signed_out)
27
+
28
+ if response.status == 401
29
+ Aromancer::Storage.set_signed_out_flag(true)
30
+ player = Aromancer::Storage.get_player
31
+ player["api_key"] = nil
32
+ Aromancer::Storage.set_player(player)
44
33
  end
45
34
  end
46
35
 
@@ -93,17 +82,9 @@ module Aromancer
93
82
 
94
83
  def self.sign_out
95
84
  result = Aromancer::Server.sign_out
96
- # clear storage
97
- Aromancer::Storage.instance.clear_cache!
98
- Aromancer::Storage.set_signed_out_flag(false)
99
85
  Aromancer::Storage.set_player(nil)
100
- Aromancer::Storage.set_legends(nil)
101
- Aromancer::Storage.set_arena(nil)
102
- Aromancer::Storage.set_stream(nil)
103
- Aromancer::Storage.set_last_error(nil)
104
-
105
86
  Aromancer::Websocket.instance.disconnect
106
-
87
+ Aromancer::Storage.recreate!
107
88
  register_error(result) if result.status != 200
108
89
  end
109
90
 
@@ -0,0 +1,134 @@
1
+ # frozen_string_literal: true
2
+
3
+ =begin
4
+
5
+ cli.rb
6
+
7
+ aromancer cli
8
+
9
+ by i2097i
10
+
11
+ =end
12
+
13
+ module Aromancer
14
+ # instance runs on main thread
15
+ class Cli
16
+ include Singleton
17
+
18
+ attr_accessor :ui_thread, :ws_thread, :ui_busy, :ws_busy, :last_render, :error_timeout
19
+
20
+ THROTTLE_MS = 0.1
21
+
22
+ # user entry point
23
+ def self.run
24
+ Aromancer::Websocket.instance
25
+ Aromancer::Storage.instance
26
+
27
+ self.instance.last_render = 0
28
+ self.instance.error_timeout = Time.now.to_i
29
+
30
+ # load player on-start
31
+ Aromancer::Api.set_active_legend_id(nil) unless Aromancer::Storage.get_player.nil?
32
+
33
+ Thread.main[:route] = :authentication
34
+ Thread.main[:menu_runners] = {}
35
+ Thread.main[:menu_options] = {}
36
+ Aromancer::Prompt.say(I18n.t("shared.welcome", version: Aromancer::VERSION))
37
+ Aromancer::Prompt.say(I18n.t("shared.server_url", url: Aromancer::Storage::get_server_url))
38
+ self.instance.start_ui_thread
39
+ self.instance.start_ws_thread
40
+ loop do
41
+ begin
42
+ self.instance.ui_thread.join
43
+ rescue NoMethodError => e
44
+ if Aromancer::Storage.get_preference(:debug)
45
+ Aromancer::Prompt.say(I18n.t("shared.last_error"))
46
+ Aromancer::Prompt.say(JSON.pretty_generate(e[:body.to_s]))
47
+ Aromancer::Prompt.say("sleeping 5 on main thread...")
48
+ sleep(5)
49
+ end
50
+ self.instance.start_ui_thread
51
+ self.instance.start_ws_thread
52
+ sleep(1)
53
+ rescue Interrupt => e
54
+ Aromancer::Prompt.say("interrupt on main thread...exiting immediately.")
55
+ exit
56
+ end
57
+ end
58
+ end
59
+
60
+ def start_ui_thread(force = false)
61
+ return if (@ui_busy || Time.now - @last_render < 2.0) && !force
62
+
63
+ @ui_busy = true
64
+ self.ui_thread&.exit
65
+ self.ui_thread&.kill
66
+ Thread.main[:menu_runners] = {}
67
+ Thread.main[:menu_options] = {}
68
+ Aromancer::Prompt.instance.reload!
69
+ self.ui_thread = Thread.new do
70
+ loop do
71
+ menu_options = Thread.main[:menu_options][Thread.main[:route]]
72
+ menu_runners = Thread.main[:menu_runners][Thread.main[:route]]
73
+ unless menu_options.nil? ||
74
+ menu_runners.nil? ||
75
+ menu_options.empty? ||
76
+ menu_runners.empty?
77
+
78
+ Aromancer::Routes.resume
79
+ sleep(THROTTLE_MS)
80
+ Aromancer.print_hud
81
+
82
+ v = Aromancer::Prompt.p.select("", menu_options, show_help: :always, cycle: true, per_page: 11)
83
+ Aromancer.print_hud
84
+ runners = menu_runners.select{|r| r[:value] == v}
85
+ runners = menu_runners.select{|r| r[:value].nil?} if runners.empty?
86
+
87
+ if runners.any?
88
+ r = runners.first
89
+ r[:proc].call(v)
90
+ end
91
+
92
+ else
93
+ sleep(THROTTLE_MS)
94
+ Aromancer::Routes.resume
95
+ end
96
+ Aromancer::Cli.instance.ui_busy = false
97
+ Aromancer::Cli.instance.last_render = Time.now
98
+ sleep(THROTTLE_MS)
99
+ end
100
+ end
101
+ end
102
+
103
+ def start_ws_thread
104
+ return if @ws_busy
105
+ @ws_busy = true
106
+ self.ws_thread&.exit
107
+ self.ws_thread&.kill
108
+ self.ws_thread = Thread.new do
109
+ Aromancer::Websocket.instance.disconnect
110
+ loop do
111
+ player = Aromancer::Storage.get_player
112
+ if player.nil? ||
113
+ player["active_legend_id"].nil?
114
+ @ws_busy = true
115
+ Aromancer::Websocket.instance.disconnect
116
+ elsif !player["api_key"].nil? &&
117
+ !player["active_legend_id"].nil? &&
118
+ (Aromancer::Websocket.instance.socket.nil? || Aromancer::Websocket.instance.socket.closed?)
119
+ # attempt websocket connection
120
+ @ws_busy = true
121
+ Aromancer::Websocket.instance.connect
122
+ if Aromancer::Storage.get_arena.nil?
123
+ Aromancer::Api.load_arena
124
+ sleep(1)
125
+ Aromancer::Cli.instance.start_ui_thread(true)
126
+ end
127
+ end
128
+ @ws_busy = false
129
+ sleep(1)
130
+ end
131
+ end
132
+ end
133
+ end
134
+ end
@@ -46,24 +46,10 @@ module Aromancer
46
46
  ).first
47
47
  end
48
48
 
49
- def self.arena_data
50
- arena = Aromancer::Storage.get_arena
51
- if arena.nil?
52
- Aromancer::Api.load_arena
53
- sleep(1)
54
- end
55
-
56
- if arena.nil?
57
- Aromancer.print_last_error("routes.legend.arena_failed")
58
- Aromancer::Prompt.p.ask(I18n.t("shared.key_press"))
59
- Aromancer::Api.set_active_legend_id(nil)
60
- else
61
- arena
62
- end
63
- end
64
-
65
49
  def self.authentication
66
- if Aromancer::Storage.get_player
50
+ unless Aromancer::Storage.get_player.nil? ||
51
+ Aromancer::Storage.get_player["api_key"].nil?
52
+
67
53
  Aromancer::Storage.set_signed_out_flag(false)
68
54
  return main_menu
69
55
  end
@@ -87,6 +73,9 @@ module Aromancer
87
73
  }},
88
74
  {value: :configure, proc: Proc.new{
89
75
  configure
76
+ }},
77
+ {value: :exit, proc: Proc.new{
78
+ exit
90
79
  }}
91
80
  ]
92
81
  Thread.main[:route] = __method__.to_sym
@@ -94,7 +83,7 @@ module Aromancer
94
83
 
95
84
  def self.configure
96
85
  cls
97
-
86
+ Aromancer.print_large_text(Thread.main[:route].to_s)
98
87
  Aromancer::Prompt.say(I18n.t("routes.configure.title", name: Aromancer.to_s.downcase))
99
88
  [
100
89
  Aromancer::Storage.get_server_url,
@@ -210,6 +199,7 @@ module Aromancer
210
199
 
211
200
  return authentication if player.nil?
212
201
 
202
+ Aromancer.print_large_text
213
203
  Thread.main[:menu_options][__method__.to_sym] ||= [
214
204
  {name: I18n.t("routes.main_menu.new_game"), value: :new},
215
205
  {name: I18n.t("routes.main_menu.load_game"), value: :load},
@@ -252,7 +242,7 @@ module Aromancer
252
242
  }},
253
243
  {value: :exit, proc: Proc.new{
254
244
  exit
255
- }},
245
+ }}
256
246
  ]
257
247
  Thread.main[:route] = __method__.to_sym
258
248
  end
@@ -277,18 +267,8 @@ module Aromancer
277
267
  ]
278
268
  Thread.main[:menu_runners][__method__.to_sym] = [
279
269
  {value: :continue, proc: Proc.new{
280
- Aromancer::Storage.instance.clear_cache!
281
270
  Aromancer::Api.create_legend(params)
282
271
  legend_menu
283
- # Aromancer::Api.get_legends
284
-
285
- # unless active_legend.nil? || active_legend["id"] != Aromancer::Storage.get_player["active_legend_id"]
286
- # Aromancer.print_last_error
287
- # sleep(1)
288
- # main_menu
289
- # else
290
- # legend_menu
291
- # end
292
272
  }},
293
273
  {value: :cancel, proc: Proc.new{
294
274
  Aromancer::Prompt.say(I18n.t("routes.new_game.cancelled"))
@@ -306,7 +286,6 @@ module Aromancer
306
286
 
307
287
  # if legend_array.nil?, load player's legends
308
288
  if legend_array.nil?
309
- Aromancer::Storage.instance.clear_cache!
310
289
  Aromancer::Api.get_legends
311
290
  legend_array = Aromancer::Storage.get_legends
312
291
  unless legend_array.any?
@@ -328,20 +307,19 @@ module Aromancer
328
307
  end
329
308
 
330
309
  Thread.main[:menu_runners][__method__.to_sym] = [
331
- {value: :back, proc: Proc.new{ main_menu }},
310
+ {value: :back, proc: Proc.new{
311
+ Aromancer::Api.set_active_legend_id(nil)
312
+ main_menu
313
+ }},
332
314
  ] + legend_array.map{|l|
333
315
  {
334
316
  value: l["id"],
335
317
  proc: Proc.new{
336
318
  Aromancer::Api.set_active_legend_id(l["id"])
319
+ sleep(1)
320
+ Aromancer::Api.get_legends
337
321
 
338
- unless active_legend.nil? || active_legend["id"] != Aromancer::Storage.get_player["active_legend_id"]
339
- Aromancer.print_last_error
340
- sleep(1)
341
- main_menu
342
- else
343
- legend_menu
344
- end
322
+ legend_menu
345
323
  }
346
324
  }
347
325
  }
@@ -370,61 +348,80 @@ module Aromancer
370
348
 
371
349
  def self.legend_menu
372
350
  cls
373
- legend
374
-
375
- legend_array = Aromancer::Storage.get_legends
376
351
  al = active_legend
377
- if !al.nil?
378
- if al["status"] == "complete"
352
+ al_status = ((al || {})["status"] || "").to_sym
353
+ arena = Aromancer::Storage.get_arena
354
+ if !al_status.nil? &&
355
+ ![:started, :paused].include?(al_status) &&
356
+ !al.nil? &&
357
+ !arena.nil?
358
+
359
+ stream(display_only: true) # display entire stream
360
+ Aromancer::Prompt.print_divider(al["value_display"])
361
+ Aromancer::Prompt.say(arena["title"])
362
+ Aromancer::Prompt.say(arena["players"].map{|p| "#{p["alias"]}(#{p["status"]}): #{p["score"]}"}.join(" | "))
363
+ Thread.main[:route] = __method__.to_sym
364
+ case al_status
365
+ when :created
366
+ Aromancer::Api.get_legends
367
+
379
368
  Thread.main[:menu_options][__method__.to_sym] = [
380
369
  {name: I18n.t("shared.key_press"), value: :key_press}
381
370
  ]
382
371
  Thread.main[:menu_runners][__method__.to_sym] = [
383
372
  {value: :key_press, proc: Proc.new{
384
- Aromancer::Prompt.say(I18n.t("routes.legend.returning_to_main"))
373
+ Aromancer::Prompt.say(I18n.t("routes.legend.returning_to_", route: :legends))
385
374
  Aromancer::Api.set_active_legend_id(nil)
375
+ sleep(1)
386
376
  legends
387
377
  }}
388
378
  ]
389
- Thread.main[:route] = __method__.to_sym
390
- return
391
- elsif al["status"] == "created"
379
+ when :complete
392
380
  Thread.main[:menu_options][__method__.to_sym] = [
393
- {name: :reload, value: :reload}, {name: I18n.t("shared.back"), value: :back}
381
+ {name: I18n.t("shared.key_press"), value: :key_press}
394
382
  ]
395
383
  Thread.main[:menu_runners][__method__.to_sym] = [
396
- {value: :reload, proc: Proc.new{ legend_menu }},
397
- {value: :back, proc: Proc.new{
384
+ {value: :key_press, proc: Proc.new{
385
+ Aromancer::Prompt.say(I18n.t("routes.legend.returning_to_", route: :legends))
398
386
  Aromancer::Api.set_active_legend_id(nil)
399
387
  legends
400
388
  }}
401
389
  ]
402
- Thread.main[:route] = __method__.to_sym
403
- return
404
- elsif active_legend["player"] && (active_legend["player"]["words"] || []).count == 0
405
- Aromancer::Api.load_arena
406
- return
407
390
  end
408
- else
409
- Thread.main[:menu_runners] = {}
410
- Thread.main[:menu_options] = {}
411
- Thread.main[:route] = :main_menu
391
+ return
392
+ end
393
+
394
+ arena = Aromancer::Storage.get_arena
395
+ if arena.nil? || arena["turn_player"].nil?
396
+ Aromancer::Prompt.say(I18n.t("shared.waiting"))
397
+ Thread.main[:menu_options][__method__.to_sym] = []
398
+ Thread.main[:menu_runners][__method__.to_sym] = []
399
+ sleep(1)
400
+ Aromancer::Api.get_legends
401
+ Aromancer::Api.load_arena
402
+ return
412
403
  end
413
404
 
405
+ legend
406
+
407
+ Thread.main[:route] = __method__.to_sym
408
+
414
409
  hide_sentences_pref = Aromancer::Storage.get_preference(:hide_sentences)
415
410
  hide_sentences_pref_value = "#{hide_sentences_pref ? "" : "hide_"}signed_sentences".to_sym
416
411
  hide_stream_pref = Aromancer::Storage.get_preference(:hide_stream)
417
412
  hide_stream_pref_value = "#{hide_stream_pref ? "" : "hide_"}title".to_sym
418
-
419
- Thread.main[:route] = __method__.to_sym
420
413
  Thread.main[:menu_options][__method__.to_sym] = [
421
- {name: I18n.t("routes.legend.unspoken_words"), value: :words},
422
- {name: I18n.t("routes.legend.spoken_words"), value: :spoken_words},
423
- {name: I18n.t("routes.legend.unsigned_sentences"), value: :unsigned_sentences},
424
414
  {name: I18n.t("routes.legend.#{hide_sentences_pref_value}"), value: hide_sentences_pref_value},
425
415
  {name: I18n.t("routes.stream.#{hide_stream_pref_value}"), value: hide_stream_pref_value},
426
416
  {name: I18n.t("routes.main_menu.title"), value: :main_menu}
427
417
  ]
418
+ if arena["turn_player"]["id"] == Aromancer::Storage.get_player["id"]
419
+ [
420
+ {name: I18n.t("routes.legend.unspoken_words"), value: :words},
421
+ {name: I18n.t("routes.legend.spoken_words"), value: :spoken_words},
422
+ {name: I18n.t("routes.legend.unsigned_sentences"), value: :unsigned_sentences}
423
+ ].reverse.each{|mo| Thread.main[:menu_options][__method__.to_sym].insert(0, mo)}
424
+ end
428
425
 
429
426
  Thread.main[:menu_runners][__method__.to_sym] = [
430
427
  {value: :words, proc: Proc.new{ words }},
@@ -447,48 +444,7 @@ module Aromancer
447
444
 
448
445
  def self.legend
449
446
  Aromancer::Prompt.say(I18n.t("shared.navigate", route: :legend)) if Aromancer::Storage.get_preference(:debug)
450
- Aromancer::Api.load_arena
451
- arena = arena_data
452
- al = active_legend
453
- al_status = ((al || {})["status"] || "").to_sym
454
- if !al.nil? &&
455
- [:created, :complete].include?(al_status)
456
-
457
- stream(display_only: true) # display entire stream
458
- Aromancer::Prompt.print_divider(al["value_display"])
459
- Aromancer::Prompt.say(arena["title"])
460
- Aromancer::Prompt.say(arena["players"].map{|p| "#{p["alias"]}(#{p["score"]})"}.join(" | "))
461
-
462
- if al_status == :complete
463
- Thread.main[:menu_options][:legend_menu] = [
464
- {name: I18n.t("shared.key_press"), value: :key_press}
465
- ]
466
- Thread.main[:menu_runners][:legend_menu] = [
467
- {value: :key_press, proc: Proc.new{
468
- Aromancer::Prompt.say(I18n.t("routes.legend.returning_to_main"))
469
- Aromancer::Api.set_active_legend_id(nil)
470
- legends
471
- }}
472
- ]
473
- else
474
- Aromancer::Api.get_legends
475
-
476
- Thread.main[:menu_options][:legend_menu] = [
477
- {name: I18n.t("shared.back"), value: :back},
478
- {name: I18n.t("shared.key_press"), value: :key_press}
479
- ]
480
- Thread.main[:menu_runners][:legend_menu] = [
481
- {value: :back, proc: Proc.new{
482
- Aromancer::Prompt.say(I18n.t("routes.legend.returning_to_main"))
483
- Aromancer::Api.set_active_legend_id(nil)
484
- legends
485
- }},
486
- {value: :key_press, proc: Proc.new{ legend_menu }}
487
- ]
488
- end
489
- return
490
- end
491
-
447
+ arena = Aromancer::Storage.get_arena
492
448
  return if arena.nil? || arena["turn_player"].nil?
493
449
 
494
450
  turn_player = arena["turn_player"]
@@ -578,7 +534,12 @@ module Aromancer
578
534
  cls
579
535
  legend
580
536
 
581
- arena = arena_data
537
+ arena = Aromancer::Storage.get_arena
538
+ if arena["player"].nil? || arena["player"]["words"].nil?
539
+ Aromancer::Api.load_arena
540
+ sleep(1)
541
+ main_menu
542
+ end
582
543
  Thread.main[:menu_options][__method__.to_sym] = [
583
544
  {name: I18n.t("shared.back"), value: :back}
584
545
  ] + arena["player"]["words"].map{|w| {name: w["value_display"], value: w["id"]}}
@@ -597,7 +558,7 @@ module Aromancer
597
558
  cls
598
559
  legend
599
560
 
600
- arena = arena_data
561
+ arena = Aromancer::Storage.get_arena
601
562
  Thread.main[:menu_options][__method__.to_sym] = [
602
563
  {name: I18n.t("shared.back"), value: :back}
603
564
  ] + arena["scroll_words"].map{|w| {name: w["value_display"], value: w["id"]}}
@@ -616,7 +577,7 @@ module Aromancer
616
577
  cls
617
578
  legend
618
579
 
619
- arena = arena_data
580
+ arena = Aromancer::Storage.get_arena
620
581
  Thread.main[:menu_options][__method__.to_sym] = [
621
582
  {name: I18n.t("shared.back"), value: :back}
622
583
  ] + arena["sentences"].map{|s| {name: s["value_display"], value: s["id"]}}
@@ -646,11 +607,12 @@ module Aromancer
646
607
  selected_word = possibilities["word"]
647
608
  for_type = possibilities["for_type"].to_sym
648
609
 
649
- arena = arena_data
610
+ arena = Aromancer::Storage.get_arena
650
611
  player_words = arena["player"]["words"]
651
612
 
652
613
  choices = [{name: I18n.t("shared.back"), value: :back}]
653
- if for_type == :player_word
614
+ if for_type == :player_word &&
615
+ arena["turn_player"]["id"] == Aromancer::Storage.get_player["id"]
654
616
 
655
617
  # show place on scroll option if selected word is from player's words
656
618
  choices << {
@@ -659,8 +621,11 @@ module Aromancer
659
621
  }
660
622
  end
661
623
 
662
- choices += options.map{|o| {name: "#{o["humanized_title"]} -> weight: #{o["total_weight"]}", value: o}}
624
+ unless arena["turn_player"]["id"] != Aromancer::Storage.get_player["id"]
625
+ choices += options.map{|o| {name: "#{o["humanized_title"]} -> weight: #{o["total_weight"]}", value: o}}
626
+ end
663
627
 
628
+ _muh_method = __method__.to_sym
664
629
  Thread.main[:menu_options][__method__.to_sym] = choices
665
630
  Thread.main[:menu_runners][__method__.to_sym] = [
666
631
  {value: :back, proc: Proc.new{
@@ -674,12 +639,24 @@ module Aromancer
674
639
  end
675
640
  }},
676
641
  {value: :player_word, proc: Proc.new{
642
+ Aromancer::Prompt.say(I18n.t("shared.waiting"))
643
+ Thread.main[:menu_options][_muh_method] = []
644
+ Thread.main[:menu_runners][_muh_method] = []
677
645
  Aromancer::Api.submit_turn(request_uuid)
678
- legend_menu
646
+ # websockets will trigger this for other player after turn is submitted
647
+ Aromancer::Api.get_legends
648
+ Aromancer::Api.load_arena
649
+ sleep(1)
679
650
  }},
680
651
  {value: nil, proc: Proc.new{|o|
652
+ Aromancer::Prompt.say(I18n.t("shared.waiting"))
653
+ Thread.main[:menu_options][_muh_method] = []
654
+ Thread.main[:menu_runners][_muh_method] = []
681
655
  Aromancer::Api.submit_turn(request_uuid, {option_uuid: o[:uuid.to_s]})
682
- legend_menu
656
+ # websockets will trigger this for other player after turn is submitted
657
+ Aromancer::Api.get_legends
658
+ Aromancer::Api.load_arena
659
+ sleep(1)
683
660
  }}
684
661
  ]
685
662
  Thread.main[:route_params] = [selected_word_id]
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ =begin
4
+
5
+ serve.rb
6
+
7
+ aromancer serve functionality.
8
+
9
+ by i2097i
10
+
11
+ =end
12
+
13
+ module Aromancer
14
+ class Serve
15
+ SERVER_NAME = :"#{Aromancer.to_s.downcase}_serve".to_s
16
+
17
+ def self.run
18
+ unless Dir.exist?(serve_dir)
19
+ system(:"git clone git@github.com:arowmancy/#{SERVER_NAME}.git #{serve_dir}".to_s)
20
+ end
21
+ FileUtils.chdir serve_dir do
22
+ branch = "v#{Aromancer::VERSION}"
23
+ system(:"git checkout #{branch}".to_s)
24
+ system(:"git pull origin #{branch}".to_s)
25
+ system(:"bin/setup".to_s)
26
+ end
27
+ end
28
+
29
+ private
30
+
31
+ def self.serve_dir
32
+ File.join(Aromancer.home_directory, SERVER_NAME)
33
+ end
34
+ end
35
+ end
@@ -22,7 +22,6 @@ module Aromancer
22
22
  def initialize(status: -1, body: {})
23
23
  @status = status
24
24
  @body = body
25
- Aromancer::Storage.instance.clear_cache!
26
25
  end
27
26
 
28
27
  def self.build(e)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  =begin
2
4
 
3
5
  storage.rb
@@ -12,18 +14,116 @@ module Aromancer
12
14
  class Storage
13
15
  include Singleton
14
16
 
17
+ VALID_KEYS = [
18
+ :arena,
19
+ :last_error,
20
+ :legends,
21
+ :player,
22
+ :preference_debug,
23
+ :preference_force_ssl,
24
+ :preference_hide_sentences,
25
+ :preference_hide_stream,
26
+ :server_url_host,
27
+ :server_url_port,
28
+ :server_url_scheme,
29
+ :signed_out_flag,
30
+ :stream,
31
+ :websocket_path,
32
+ :websocket_scheme
33
+ ]
34
+
35
+ PERSISTED_KEYS = [
36
+ :last_error,
37
+ :player,
38
+ :preference_debug,
39
+ :preference_force_ssl,
40
+ :preference_hide_sentences,
41
+ :preference_hide_stream,
42
+ :server_url_host,
43
+ :server_url_port,
44
+ :server_url_scheme,
45
+ :websocket_path,
46
+ :websocket_scheme
47
+ ]
48
+
49
+ URI_KEYS = [
50
+ :scheme,
51
+ :host,
52
+ :port
53
+ ]
54
+
15
55
  DEFAULT_SERVER = "http://127.0.0.1"
16
56
 
17
- attr_accessor :storage_open, :cache
57
+ DEFAULT_PREFERENCES = {
58
+ preference_debug: false,
59
+ preference_force_ssl: false,
60
+ preference_hide_sentences: true,
61
+ preference_hide_stream: true,
62
+ }
63
+
64
+ attr_accessor :storage_open, :cache, :clear_flag, :parser_error
18
65
 
19
66
  def initialize
67
+ reload!
68
+ ensure_default_preferences_exist
69
+ end
70
+
71
+ def ensure_default_preferences_exist
72
+ DEFAULT_PREFERENCES.keys.each{|d_key|
73
+ current_value = get_key(d_key)
74
+ if current_value.nil? || ![TrueClass, FalseClass].include?(current_value.class)
75
+ set_key(d_key.to_s, DEFAULT_PREFERENCES[d_key])
76
+ end
77
+ }
78
+ end
79
+
80
+ def reload!
81
+ @storage_open = false
82
+ @clear_flag = false
83
+ @parser_error = nil
20
84
  clear_cache!
21
85
  FileUtils.mkdir_p(Aromancer.home_directory)
22
86
  end
23
87
 
24
88
  def clear_cache!
89
+ if @storage_open
90
+ @clear_flag = true
91
+ return
92
+ end
93
+ @clear_flag = false
25
94
  @storage_open = false
26
- @cache = {}
95
+
96
+ @cache = {} # clear'd
97
+ end
98
+
99
+ def self.snapshot_config
100
+ current_config = {}
101
+
102
+ PERSISTED_KEYS.each{|p_key|
103
+ current_config[p_key] = Aromancer::Storage.instance.get_key(p_key)
104
+ }
105
+
106
+ current_config
107
+ end
108
+
109
+ def self.valid_config?(config)
110
+ return false unless config.kind_of?(Hash)
111
+
112
+ config.keys.all?{|k| PERSISTED_KEYS.include?(k.to_sym)}
113
+ end
114
+
115
+ def self.restore_config(config)
116
+ return nil unless valid_config?(config)
117
+ PERSISTED_KEYS.each{|p_key|
118
+ Aromancer::Storage.instance.set_key(p_key, config[p_key])
119
+ }
120
+ end
121
+
122
+ def self.recreate!
123
+ current_config = snapshot_config
124
+ FileUtils.rm_rf(storage_filepath)
125
+ Aromancer::Storage.instance.reload!
126
+ restore_config(current_config)
27
127
  end
28
128
 
29
129
  def self.storage_filepath
@@ -50,33 +150,33 @@ module Aromancer
50
150
 
51
151
  def self.get_preference(preference, default: nil)
52
152
  p_key = "preference_#{preference}"
53
- p = instance.get_key(p_key)
54
- if p.nil?
55
- instance.set_key(p_key, default)
56
- return instance.get_key(p_key)
153
+ pref = Aromancer::Storage.instance.get_key(p_key)
154
+ if pref.nil? && !default.nil?
155
+ Aromancer::Storage.instance.set_key(p_key, default)
156
+ pref = Aromancer::Storage.instance.get_key(p_key)
57
157
  end
58
- p
158
+ pref
59
159
  end
60
160
 
61
161
  def self.set_preference(preference, value)
62
162
  p_key = "preference_#{preference}"
63
- instance.set_key(p_key, value)
163
+ Aromancer::Storage.instance.set_key(p_key, value)
64
164
  end
65
165
 
66
166
  def self.toggle_preference(key)
67
- v = get_preference(key)
167
+ v = get_preference(key, default: DEFAULT_PREFERENCES["preference_#{key}"])
68
168
  set_preference(key, !v)
69
169
  end
70
170
 
71
171
  def self.toggle_force_ssl
72
172
  toggle_preference(:force_ssl)
73
173
  v = get_preference(:force_ssl)
74
- self.instance.set_key(:server_url_scheme, "http#{v ? :s : ""}")
75
- self.instance.set_key(:websocket_scheme, "ws#{v ? :s : ""}")
174
+ Aromancer::Storage.set_key(:server_url_scheme, "http#{v ? :s : ""}")
175
+ Aromancer::Storage.set_key(:websocket_scheme, "ws#{v ? :s : ""}")
76
176
  end
77
177
 
78
178
  def self.set_signed_out_flag(flag)
79
- Aromancer::Storage.instance.set_key(:signed_out_flag, flag)
179
+ instance.set_key(:signed_out_flag, flag)
80
180
  end
81
181
 
82
182
  def self.get_signed_out_flag
@@ -84,7 +184,7 @@ module Aromancer
84
184
  end
85
185
 
86
186
  def self.set_player(player_hash)
87
- Aromancer::Storage.instance.set_key(:player, player_hash)
187
+ instance.set_key(:player, player_hash)
88
188
  end
89
189
 
90
190
  def self.get_player
@@ -92,7 +192,7 @@ module Aromancer
92
192
  end
93
193
 
94
194
  def self.set_legends(legends_array)
95
- Aromancer::Storage.instance.set_key(:legends, legends_array)
195
+ instance.set_key(:legends, legends_array)
96
196
  end
97
197
 
98
198
  def self.get_legends
@@ -100,7 +200,7 @@ module Aromancer
100
200
  end
101
201
 
102
202
  def self.set_arena(arena_hash)
103
- Aromancer::Storage.instance.set_key(:arena, arena_hash)
203
+ instance.set_key(:arena, arena_hash)
104
204
  end
105
205
 
106
206
  def self.get_arena
@@ -108,7 +208,7 @@ module Aromancer
108
208
  end
109
209
 
110
210
  def self.set_stream(stream_array)
111
- Aromancer::Storage.instance.set_key(:stream, stream_array)
211
+ instance.set_key(:stream, stream_array)
112
212
  end
113
213
 
114
214
  def self.get_stream
@@ -116,25 +216,18 @@ module Aromancer
116
216
  end
117
217
 
118
218
  def self.set_last_error(error_hash)
119
- Aromancer::Storage.instance.set_key(:last_error, error_hash)
219
+ instance.set_key(:last_error, error_hash)
120
220
  end
121
221
 
122
222
  def self.get_last_error
123
223
  Aromancer::Storage.instance.get_key(:last_error)
124
224
  end
125
225
 
126
- URI_KEYS = [
127
- :scheme,
128
- :host,
129
- :port
130
- ]
131
-
132
226
  def self.set_server_url(server_url)
133
227
  uri = URI.parse(server_url)
134
228
  if uri.kind_of?(URI::HTTP) || uri.kind_of?(URI::HTTPS)
135
229
  URI_KEYS.each{|k|
136
- uri.kind_of?(URI::HTTP) && get_preference(:force_ssl) ? toggle_force_ssl : nil
137
- Aromancer::Storage.instance.set_key("server_url_#{k}", uri.select(k).first)
230
+ instance.set_key("server_url_#{k}", uri.select(k).first)
138
231
  }
139
232
  elsif get_server_url.nil?
140
233
  set_server_url(DEFAULT_SERVER)
@@ -159,12 +252,12 @@ module Aromancer
159
252
  def self.get_websocket_url
160
253
  # default scheme is ws
161
254
  w_scheme = Aromancer::Storage.instance.get_key(:websocket_scheme)
162
- Aromancer::Storage.instance.set_key(:websocket_scheme, :ws) if w_scheme.nil?
255
+ instance.set_key(:websocket_scheme, :ws) if w_scheme.nil?
163
256
  w_scheme = Aromancer::Storage.instance.get_key(:websocket_scheme)
164
257
 
165
258
  # default path is broadcast
166
259
  w_path = Aromancer::Storage.instance.get_key(:websocket_path)
167
- Aromancer::Storage.instance.set_key(:websocket_path, :broadcast) if w_path.nil?
260
+ instance.set_key(:websocket_path, :broadcast) if w_path.nil?
168
261
  w_path = Aromancer::Storage.instance.get_key(:websocket_path)
169
262
 
170
263
  # asserts host and port not nil
@@ -180,7 +273,9 @@ module Aromancer
180
273
 
181
274
  # ensure default for key exists
182
275
  @cache[key] = {value: nil, persisted: false} unless @cache.keys.include?(key)
183
-
276
+ if false && (@cache[:preference_debug.to_s] || {value: DEFAULT_PREFERENCES[:preference_debug]})[:value]
277
+ Aromancer::Prompt.say("#{__method__} > @cache[#{key}]=#{@cache[key]}")
278
+ end
184
279
  # return cache value if storage open or the cache value is persisted
185
280
  return @cache[key][:value] if @storage_open || @cache[key][:persisted]
186
281
 
@@ -188,12 +283,17 @@ module Aromancer
188
283
  @storage_open = true
189
284
  storage_contents = {}
190
285
  begin
286
+ @parser_error = nil
191
287
  # read storage
192
- storage_contents = JSON.parse(File.read(Aromancer::Storage.storage_filepath))
288
+ storage_contents = JSON.parse(File.read(Aromancer::Storage.storage_filepath, encoding: "UTF-8"))
193
289
  rescue StandardError => e
194
290
  @storage_open = false
195
- FileUtils.rm(Aromancer::Storage.storage_filepath)
196
- return
291
+ if @parser_error.nil?
292
+ @parser_error = e
293
+ return get_key(key)
294
+ else
295
+ raise @parser_error
296
+ end
197
297
  end
198
298
 
199
299
  # only update the cache value if present in storage
@@ -203,20 +303,26 @@ module Aromancer
203
303
  @cache[key][:persisted] = false
204
304
  end
205
305
 
306
+ value = @cache[key][:value]
206
307
  @storage_open = false
207
- @cache[key][:value]
308
+ clear_cache! if @clear_flag
309
+ return value
208
310
  end
209
311
 
210
312
  def set_key(key, value)
211
313
  key = key.to_s
212
314
 
213
315
  # ensure default for key exists
214
- @cache[key] = {value: nil, persisted: false} unless @cache.keys.include?(key)
316
+ @cache[key] = {value: value, persisted: false} unless @cache.keys.include?(key)
215
317
 
216
318
  # assign new cache value and set persisted to false
217
319
  @cache[key][:value] = value
218
320
  @cache[key][:persisted] = false
219
321
 
322
+ if @storage_open && (@cache[:preference_debug.to_s] || {value: DEFAULT_PREFERENCES[:preference_debug]})[:value]
323
+ puts Thread.current.backtrace
324
+ Aromancer::Prompt.say("#{__method__} > @cache[#{key}]=#{@cache[key]}")
325
+ end
220
326
  # return if storage open
221
327
  # non-persisted cache values update next cycle
222
328
  return if @storage_open
@@ -225,12 +331,17 @@ module Aromancer
225
331
  @storage_open = true
226
332
  storage_contents = {}
227
333
  begin
334
+ @parser_error = nil
228
335
  # read storage
229
- storage_contents = JSON.parse(File.read(Aromancer::Storage.storage_filepath))
336
+ storage_contents = JSON.parse(File.read(Aromancer::Storage.storage_filepath, encoding: "UTF-8"))
230
337
  rescue StandardError => e
231
338
  @storage_open = false
232
- FileUtils.rm(Aromancer::Storage.storage_filepath)
233
- return
339
+ if @parser_error.nil?
340
+ @parser_error = e
341
+ return get_key(key)
342
+ else
343
+ raise @parser_error
344
+ end
234
345
  end
235
346
 
236
347
  # update storage and cache
@@ -246,8 +357,13 @@ module Aromancer
246
357
  f.write(storage_contents.to_json)
247
358
  end
248
359
 
360
+ if (@cache[:preference_debug.to_s] || {value: DEFAULT_PREFERENCES[:preference_debug]})[:value]
361
+ Aromancer::Prompt.say("#{__method__} > @cache[#{key}]=#{@cache[key]}")
362
+ end
363
+
249
364
  # close storage
250
365
  @storage_open = false
366
+ clear_cache! if @clear_flag
251
367
  end
252
368
  end
253
369
  end
@@ -11,5 +11,5 @@
11
11
  =end
12
12
 
13
13
  module Aromancer
14
- VERSION = "0.0.7"
14
+ VERSION = "0.0.8"
15
15
  end
@@ -22,9 +22,18 @@ module Aromancer
22
22
 
23
23
  attr_accessor :socket
24
24
 
25
+ def self.channel_identifier
26
+ {identifier: {channel: :PlayerChannel.to_s, id: Aromancer::Storage.get_player["id"]}.to_json}
27
+ end
28
+
25
29
  def connect
26
- return unless self.socket.nil?
27
- return if Aromancer::Storage.get_player["api_key"].nil?
30
+ if !self.socket.nil? && self.socket.closed?
31
+ disconnect
32
+ end
33
+
34
+ return if !self.socket.nil? ||
35
+ Aromancer::Storage.get_player["api_key"].nil? ||
36
+ Aromancer::Storage.get_player["active_legend_id"].nil?
28
37
 
29
38
  begin
30
39
  self.socket = WebSocket::Client::Simple.connect(Aromancer::Storage.get_websocket_url, {headers: Aromancer::Server.auth_header})
@@ -45,8 +54,10 @@ module Aromancer
45
54
  legend_id = payload["message"]["id"]
46
55
  lid = Aromancer::Storage.get_player&.symbolize_keys[:active_legend_id]
47
56
  if !lid.nil? && lid == legend_id.to_i
57
+ Aromancer::Storage.instance.clear_cache!
58
+ Aromancer::Api.get_legends
48
59
  Aromancer::Api.load_arena
49
- sleep(0.5)
60
+ sleep(1)
50
61
  Aromancer::Cli.instance.start_ui_thread
51
62
  end
52
63
  when :legends
@@ -57,10 +68,7 @@ module Aromancer
57
68
  end
58
69
 
59
70
  self.socket.on :open do
60
- r = {
61
- command: "subscribe",
62
- identifier: {channel: "PlayerChannel"}.to_json
63
- }.to_json
71
+ r = {"command": :subscribe.to_s}.merge(Aromancer::Websocket.channel_identifier).to_json
64
72
  Aromancer::Websocket.instance.socket.send(r)
65
73
  end
66
74
 
@@ -69,7 +77,10 @@ module Aromancer
69
77
  end
70
78
 
71
79
  self.socket.on :error do |e|
72
- puts e.inspect
80
+ if Aromancer::Storage.get_preference(:debug)
81
+ Aromancer::Prompt.say("#{self.class.name} socket.on :error")
82
+ Aromancer::Prompt.say(e.inspect)
83
+ end
73
84
  Aromancer::Websocket.instance.disconnect
74
85
  end
75
86
  end
@@ -78,5 +89,15 @@ module Aromancer
78
89
  self.socket&.close
79
90
  self.socket = nil
80
91
  end
92
+
93
+ # todo: websockets only in-game
94
+ # https://docs.anycable.io/misc/action_cable_protocol#perform-actions
95
+ # def self.set_active_legend_id(id)
96
+ # return if self.instance.socket.nil?
97
+ # r = {"command": :message.to_s}.merge(Aromancer::Websocket.channel_identifier).merge({
98
+ # data: {action: :player.to_s, active_legend_id: id}.to_json
99
+ # }).to_json
100
+ # self.instance.socket.send(r)
101
+ # end
81
102
  end
82
103
  end
data/lib/aromancer.rb CHANGED
@@ -14,8 +14,7 @@ require_relative :reiquire.to_s
14
14
  Reiquire::aromancer
15
15
 
16
16
  module Aromancer
17
- THROTTLE_MS = 0.1
18
- ERROR_EXPIRATION = 7.77.to_f
17
+ ERROR_EXPIRATION = 11.11.to_f
19
18
  FONT = TTY::Font.new(:standard)
20
19
  DATE_FORMAT = "%A %d %b %Y %I:%M:%S %p"
21
20
  TIME_FORMAT = "%I:%M:%S %p"
@@ -25,130 +24,8 @@ module Aromancer
25
24
  check: "\u2713"
26
25
  }
27
26
 
28
- class Serve
29
- def self.run
30
- unless Dir.exist?("#{Aromancer.to_s.downcase}_serve")
31
- system("git clone git@github.com:arowmancy/aromancer_serve.git")
32
- end
33
- FileUtils.chdir :aromancer_serve.to_s do
34
- system("bin/setup")
35
- end
36
- end
37
- end
38
-
39
- # instance runs on main thread
40
- class Cli
41
- include Singleton
42
-
43
- attr_accessor :ui_thread, :ws_thread, :busy, :ws_busy, :last_render, :error_timeout
44
-
45
- # user entry point
46
- def self.run
47
- # ensure preferences exist
48
- Aromancer::Storage.get_preference(:debug, default: false)
49
- Aromancer::Storage.get_preference(:force_ssl, default: true)
50
- Aromancer::Storage.get_preference(:hide_sentences, default: true)
51
- Aromancer::Storage.get_preference(:hide_stream, default: true)
52
-
53
- self.instance.last_render = 0
54
- self.instance.error_timeout = Time.now.to_i
55
-
56
- # load player on-start
57
- Aromancer::Api.set_active_legend_id(nil) unless Aromancer::Storage.get_player.nil?
58
-
59
- Thread.main[:route] = :authentication
60
- Thread.main[:menu_runners] = {}
61
- Thread.main[:menu_options] = {}
62
- Aromancer::Prompt.say(I18n.t("shared.welcome", version: Aromancer::VERSION))
63
- Aromancer::Prompt.say(I18n.t("shared.server_url", url: Aromancer::Storage::get_server_url))
64
- self.instance.start_ui_thread
65
- self.instance.start_ws_thread
66
- loop do
67
- begin
68
- self.instance.ui_thread.join
69
- rescue NoMethodError => e
70
- if Aromancer::Storage.get_preference(:debug)
71
- Aromancer::Prompt.say(I18n.t("shared.last_error"))
72
- Aromancer::Prompt.say(JSON.pretty_generate(e[:body.to_s]))
73
- Aromancer::Prompt.say("sleeping 5 on main thread...")
74
- sleep(5)
75
- end
76
- self.instance.start_ui_thread
77
- self.instance.start_ws_thread
78
- sleep(1)
79
- rescue Interrupt => e
80
- Aromancer::Prompt.say("interrupt on main thread...exiting immediately.")
81
- exit
82
- end
83
- end
84
- end
85
-
86
- def get_view(view_key)
87
- Thread.main[:menu_options][view_key.to_sym]
88
- end
89
-
90
- def start_ui_thread(force = false)
91
- return if (@busy || Time.now - @last_render < 2.0) && !force
92
-
93
- @busy = true
94
- self.ui_thread&.exit
95
- self.ui_thread&.kill
96
- Thread.main[:menu_runners] = {}
97
- Thread.main[:menu_options] = {}
98
- Aromancer::Prompt.instance.reload!
99
- self.ui_thread = Thread.new do
100
- loop do
101
- menu_options = Thread.main[:menu_options][Thread.main[:route]]
102
- menu_runners = Thread.main[:menu_runners][Thread.main[:route]]
103
- unless menu_options.nil? || menu_runners.nil?
104
- Aromancer::Routes.resume
105
- Aromancer.print_hud
106
- sleep(THROTTLE_MS)
107
- v = Aromancer::Prompt.p.select("", menu_options, show_help: :always, cycle: true, per_page: 11)
108
- runners = menu_runners.select{|r| r[:value] == v}
109
- runners = menu_runners.select{|r| r[:value].nil?} if runners.empty?
110
-
111
- if runners.any?
112
- r = runners.first
113
- r[:proc].call(v)
114
- end
115
-
116
- else
117
- Aromancer::Routes.resume
118
- end
119
- Aromancer::Cli.instance.busy = false
120
- Aromancer::Cli.instance.last_render = Time.now
121
- sleep(THROTTLE_MS)
122
- end
123
- end
124
- end
125
-
126
- def start_ws_thread
127
- return if @ws_busy
128
- @ws_busy = true
129
- self.ws_thread.kill unless self.ws_thread.nil?
130
- self.ws_thread = Thread.new do
131
- Aromancer::Websocket.instance.disconnect
132
- loop do
133
- player = Aromancer::Storage.get_player
134
- if player.nil?
135
- @ws_busy = true
136
- Aromancer::Websocket.instance.disconnect
137
- elsif !player["api_key"].nil?
138
- # attempt websocket connection
139
- @ws_busy = true
140
- Aromancer::Websocket.instance.connect
141
- end
142
- @ws_busy = false
143
- sleep(4)
144
- end
145
- end
146
- end
147
-
148
- end
149
-
150
- def self.get_font(text = :aromancer.to_s, letter_spacing = 1)
151
- Aromancer::FONT.write(text, letter_spacing: letter_spacing).split("\n")
27
+ def self.print_large_text(text = Aromancer.to_s.downcase, letter_spacing = 1)
28
+ Aromancer::Prompt.say(Aromancer::FONT.write(text, letter_spacing: letter_spacing))
152
29
  end
153
30
 
154
31
  def self.home_directory
@@ -165,7 +42,10 @@ module Aromancer
165
42
 
166
43
  def self.print_hud
167
44
  print_last_error
168
- websocket_status = "#{Aromancer::Storage.instance.get_key(:websocket_scheme)}: #{Aromancer::Websocket.instance.socket.nil? ? UNICODE[:close] : UNICODE[:check] }"
45
+ ws_status_symbol = (
46
+ Aromancer::Websocket.instance.socket.nil? || Aromancer::Websocket.instance.socket.closed?
47
+ ) ? UNICODE[:close] : UNICODE[:check]
48
+ websocket_status = "#{Aromancer::Storage.instance.get_key(:websocket_scheme)}: #{ws_status_symbol}"
169
49
  player_status = if Aromancer::Storage.get_player.nil?
170
50
  I18n.t("routes.main_menu.not_signed_in")
171
51
  else
@@ -184,14 +64,15 @@ module Aromancer
184
64
  return
185
65
  end
186
66
 
187
- last_error = Aromancer::Storage.get_last_error
188
- return if last_error.nil?
67
+ last_error = Aromancer::Storage.get_last_error&.symbolize_keys
189
68
 
190
- last_error = last_error.symbolize_keys
69
+ return if last_error.nil?
191
70
  message = last_error[:body]
192
71
  begin
193
72
  message = JSON.parse(message).symbolize_keys
194
73
  message = message[:error_message] if message.keys.include?(:error_message)
74
+ rescue TypeError => e
75
+ message = last_error[:body]
195
76
  rescue JSON::ParserError => e
196
77
  message = last_error[:body]
197
78
  end
data/lib/reiquire.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  =begin
2
4
 
3
5
  reiquire.rb
data/listen.rb CHANGED
@@ -1,10 +1,6 @@
1
1
  require :listen.to_s
2
2
 
3
3
  listener = Listen.to(*[:lib.to_s, :bin.to_s, :spec.to_s, :locale.to_s], only: /[\.rb\.yml]/) {|modified, added, removed|
4
- # puts modified
5
- # puts added
6
- # puts removed
7
- # system("exec rake make")
8
4
  system("exec rake install")
9
5
  }
10
6
  listener.start
data/locale/en.yml CHANGED
@@ -13,6 +13,7 @@ en:
13
13
  update_confirm: "confirm update [%{new_value}] (enter y): "
14
14
  signed_in_as: "authenticated as %{as}"
15
15
  last_error: "error (%{status}). %{error}"
16
+ waiting: "waiting..."
16
17
  back: "back"
17
18
  exit: "exit"
18
19
  routes:
@@ -68,7 +69,7 @@ en:
68
69
  place_on_scroll: "place %{word} on the scroll."
69
70
  options_for_word: "available options for the %{word} word."
70
71
  last_error: "last error"
71
- returning_to_main: "returning to main menu..."
72
+ returning_to_: "returning to %{route}..."
72
73
  your_alchemy: "alchemy"
73
74
  legends:
74
75
  help: ""
@@ -0,0 +1,5 @@
1
+ #!/bin/sh
2
+
3
+ echo "pre-commit hook"
4
+ echo "rake build:checksum && git add checksums/"
5
+ rake build:checksum && git add checksums/
data/tasks/make.rake CHANGED
@@ -1,4 +1,4 @@
1
- desc "bundle graph && rake install && rspec --backtrace"
1
+ desc "rake install"
2
2
  task :make do
3
- exec "bundle graph && rake install && rspec --backtrace"
3
+ exec "rake install"
4
4
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: aromancer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.7
4
+ version: 0.0.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - i2097i
@@ -227,6 +227,7 @@ executables:
227
227
  extensions: []
228
228
  extra_rdoc_files: []
229
229
  files:
230
+ - ".DS_Store"
230
231
  - ".gitignore"
231
232
  - ".rspec"
232
233
  - ".ruby-version"
@@ -235,7 +236,6 @@ files:
235
236
  - Gemfile
236
237
  - Gemfile.lock
237
238
  - LICENSE
238
- - LICENSE.txt
239
239
  - README.md
240
240
  - Rakefile
241
241
  - aromancer.gemspec
@@ -247,12 +247,14 @@ files:
247
247
  - checksums/aromancer-0.0.5.gem.sha512
248
248
  - checksums/aromancer-0.0.6.gem.sha512
249
249
  - checksums/aromancer-0.0.7.gem.sha512
250
- - gem_graph.png
250
+ - checksums/aromancer-0.0.8.gem.sha512
251
251
  - lib/aromancer.rb
252
252
  - lib/aromancer/.DS_Store
253
253
  - lib/aromancer/api.rb
254
+ - lib/aromancer/cli.rb
254
255
  - lib/aromancer/prompt.rb
255
256
  - lib/aromancer/routes.rb
257
+ - lib/aromancer/serve.rb
256
258
  - lib/aromancer/server.rb
257
259
  - lib/aromancer/storage.rb
258
260
  - lib/aromancer/version.rb
@@ -260,6 +262,7 @@ files:
260
262
  - lib/reiquire.rb
261
263
  - listen.rb
262
264
  - locale/en.yml
265
+ - pre-commit.git.template
263
266
  - sig/aromancer.rbs
264
267
  - tasks/console.rake
265
268
  - tasks/make.rake
data/LICENSE.txt DELETED
@@ -1,21 +0,0 @@
1
- The MIT License (MIT)
2
-
3
- Copyright (c) 2026 i2097i
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
13
- all 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
21
- THE SOFTWARE.
data/gem_graph.png DELETED
Binary file