aromancer 0.0.3 → 0.0.5

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.
@@ -13,8 +13,9 @@
13
13
  module Aromancer
14
14
  class Server
15
15
  include Singleton
16
- SERVER_HOST_FILE = ".aromancer_server_host"
17
- SERVER_URL = File.exist?(SERVER_HOST_FILE) ? File.read(SERVER_HOST_FILE) : "http://localhost:3000"
16
+
17
+ DEVELOP_SERVER_URL = "http://localhost:3000"
18
+
18
19
  HEADERS = { "Content-Type": "application/json" }
19
20
 
20
21
  class InternalError
@@ -23,12 +24,13 @@ module Aromancer
23
24
  def initialize(status: -1, body: {})
24
25
  @status = status
25
26
  @body = body
27
+ Aromancer::Storage.instance.clear_cache!
26
28
  end
27
29
 
28
30
  def self.build(e)
29
31
  InternalError.new(
30
32
  status: -1,
31
- body: {error_message: e.message[0..78]}.to_json
33
+ body: {error_message: e.inspect.to_s}.to_json
32
34
  )
33
35
  end
34
36
  end
@@ -42,7 +44,7 @@ module Aromancer
42
44
  def self.sign_in(email_address, password)
43
45
  begin
44
46
  Faraday.new(
45
- url: SERVER_URL,
47
+ url: Aromancer::Storage.get_server_url,
46
48
  headers: HEADERS,
47
49
  params: {
48
50
  email_address: email_address,
@@ -57,7 +59,7 @@ module Aromancer
57
59
  def self.sign_up(email_address, password, password_confirmation)
58
60
  begin
59
61
  Faraday.new(
60
- url: SERVER_URL,
62
+ url: Aromancer::Storage.get_server_url,
61
63
  headers: HEADERS,
62
64
  params: {
63
65
  user: {
@@ -75,7 +77,7 @@ module Aromancer
75
77
  def self.sign_out
76
78
  begin
77
79
  Faraday.new(
78
- url: SERVER_URL,
80
+ url: Aromancer::Storage.get_server_url,
79
81
  headers: HEADERS,
80
82
  params: api_key_param
81
83
  ).delete("/session")
@@ -87,7 +89,7 @@ module Aromancer
87
89
  def self.create_legend(params)
88
90
  begin
89
91
  Faraday.new(
90
- url: SERVER_URL,
92
+ url: Aromancer::Storage.get_server_url,
91
93
  headers: HEADERS,
92
94
  params: params.merge(api_key_param)
93
95
  ).post("/legends")
@@ -99,7 +101,7 @@ module Aromancer
99
101
  def self.get_legends
100
102
  begin
101
103
  Faraday.new(
102
- url: SERVER_URL,
104
+ url: Aromancer::Storage.get_server_url,
103
105
  headers: HEADERS,
104
106
  params: api_key_param
105
107
  ).get("/legends")
@@ -111,7 +113,7 @@ module Aromancer
111
113
  def self.update_player(params)
112
114
  begin
113
115
  Faraday.new(
114
- url: SERVER_URL,
116
+ url: Aromancer::Storage.get_server_url,
115
117
  headers: HEADERS,
116
118
  params: params.merge(api_key_param)
117
119
  ).put("/players/#{Aromancer::Storage.get_player["id"]}")
@@ -125,7 +127,7 @@ module Aromancer
125
127
 
126
128
  begin
127
129
  Faraday.new(
128
- url: SERVER_URL,
130
+ url: Aromancer::Storage.get_server_url,
129
131
  headers: HEADERS,
130
132
  params: api_key_param
131
133
  ).get("/legends/#{legend_id}/arena")
@@ -139,7 +141,7 @@ module Aromancer
139
141
 
140
142
  begin
141
143
  Faraday.new(
142
- url: SERVER_URL,
144
+ url: Aromancer::Storage.get_server_url,
143
145
  headers: HEADERS,
144
146
  params: api_key_param
145
147
  ).get("/legends/#{legend_id}/stream")
@@ -152,7 +154,7 @@ module Aromancer
152
154
  legend_id = Aromancer::Storage.get_player["active_legend_id"]
153
155
  begin
154
156
  Faraday.new(
155
- url: SERVER_URL,
157
+ url: Aromancer::Storage.get_server_url,
156
158
  headers: HEADERS,
157
159
  params: api_key_param
158
160
  ).get("/legends/#{legend_id}/words/#{selected_word_id}/possibilities")
@@ -165,7 +167,7 @@ module Aromancer
165
167
  legend_id = Aromancer::Storage.get_player["active_legend_id"]
166
168
  begin
167
169
  Faraday.new(
168
- url: SERVER_URL,
170
+ url: Aromancer::Storage.get_server_url,
169
171
  headers: HEADERS,
170
172
  params: params.merge(api_key_param)
171
173
  ).post("/legends/#{legend_id}/arena")
@@ -178,7 +180,7 @@ module Aromancer
178
180
  legend_id = Aromancer::Storage.get_player["active_legend_id"]
179
181
  begin
180
182
  Faraday.new(
181
- url: SERVER_URL,
183
+ url: Aromancer::Storage.get_server_url,
182
184
  headers: HEADERS,
183
185
  params: params.merge(api_key_param)
184
186
  ).put("/legends/#{legend_id}/arena")
@@ -12,10 +12,18 @@ module Aromancer
12
12
  class Storage
13
13
  include Singleton
14
14
 
15
+ attr_accessor :storage_open, :cache
16
+
15
17
  def initialize
18
+ clear_cache!
16
19
  FileUtils.mkdir_p(Aromancer.home_directory)
17
20
  end
18
21
 
22
+ def clear_cache!
23
+ @storage_open = false
24
+ @cache = {}
25
+ end
26
+
19
27
  def self.storage_filepath
20
28
  filepath = File.join(Aromancer.home_directory, "storage.json")
21
29
 
@@ -53,6 +61,13 @@ module Aromancer
53
61
  instance.set_key(p_key, value)
54
62
  end
55
63
 
64
+ def self.toggle_force_ssl
65
+ set_preference(:force_ssl, !get_preference(:force_ssl))
66
+ v = get_preference(:force_ssl)
67
+ self.instance.set_key(:server_url_scheme, "http#{v ? :s : ""}")
68
+ self.instance.set_key(:websocket_scheme, "ws#{v ? :s : ""}")
69
+ end
70
+
56
71
  def self.set_signed_out_flag(flag)
57
72
  Aromancer::Storage.instance.set_key(:signed_out_flag, flag)
58
73
  end
@@ -101,20 +116,131 @@ module Aromancer
101
116
  Aromancer::Storage.instance.get_key(:last_error)
102
117
  end
103
118
 
119
+ URI_KEYS = [
120
+ :scheme,
121
+ :host,
122
+ :port
123
+ ]
124
+
125
+ def self.set_server_url(server_url)
126
+ uri = URI.parse(server_url)
127
+ if uri.kind_of?(URI::HTTP) or uri.kind_of?(URI::HTTPS)
128
+ URI_KEYS.each{|k|
129
+ Aromancer::Storage.instance.set_key("server_url_#{k}", uri.select(k).first)
130
+ }
131
+ else
132
+ set_server_url(Aromancer::Server::DEVELOP_SERVER_URL)
133
+ end
134
+ end
135
+
136
+ def self.get_server_url
137
+ set_server_url(Aromancer::Server::DEVELOP_SERVER_URL) if URI_KEYS.all?{|k| Aromancer::Storage.instance.get_key("server_url_#{k}").nil?}
138
+ URI.parse(URI_KEYS.map{|k|
139
+ c = Aromancer::Storage.instance.get_key("server_url_#{k}")
140
+ case k
141
+ when :scheme
142
+ "#{c}://"
143
+ when :host
144
+ c
145
+ when :port
146
+ ":#{c}"
147
+ end
148
+ }.join("")).to_s
149
+ end
150
+
151
+ def self.get_websocket_url
152
+ # default scheme is ws
153
+ w_scheme = Aromancer::Storage.instance.get_key(:websocket_scheme)
154
+ Aromancer::Storage.instance.set_key(:websocket_scheme, :ws) if w_scheme.nil?
155
+ w_scheme = Aromancer::Storage.instance.get_key(:websocket_scheme)
156
+
157
+ # default path is broadcast
158
+ w_path = Aromancer::Storage.instance.get_key(:websocket_path)
159
+ Aromancer::Storage.instance.set_key(:websocket_path, :broadcast) if w_path.nil?
160
+ w_path = Aromancer::Storage.instance.get_key(:websocket_path)
161
+
162
+ # asserts host and port not nil
163
+ # assumes same-as-server host and port for now
164
+ host = Aromancer::Storage.instance.get_key(:server_url_host)
165
+ port = Aromancer::Storage.instance.get_key(:server_url_port)
166
+
167
+ URI.parse("#{w_scheme}://#{host}:#{port}/#{w_path}").to_s
168
+ end
169
+
104
170
  def get_key(key)
105
- storage_contents = JSON.parse(File.read(Aromancer::Storage.storage_filepath))
106
- if storage_contents.keys.include?(key.to_s)
107
- return storage_contents[key.to_s]
171
+ key = key.to_s
172
+
173
+ # ensure default for key exists
174
+ @cache[key] = {value: nil, persisted: false} unless @cache.keys.include?(key)
175
+
176
+ # return cache value if storage open or the cache value is persisted
177
+ return @cache[key][:value] if @storage_open || @cache[key][:persisted]
178
+
179
+ # open storage
180
+ @storage_open = true
181
+ storage_contents = {}
182
+ begin
183
+ # read storage
184
+ storage_contents = JSON.parse(File.read(Aromancer::Storage.storage_filepath))
185
+ rescue StandardError => e
186
+ @storage_open = false
187
+ FileUtils.rm(Aromancer::Storage.storage_filepath)
188
+ return
108
189
  end
190
+ @storage_open = false
191
+
192
+ # only update the cache value if present in storage
193
+ if storage_contents.keys.include?(key)
194
+ @cache[key][:value] = storage_contents[key]
195
+ @cache[key][:persisted] = true
196
+ else
197
+ @cache[key][:persisted] = false
198
+ end
199
+
200
+ @cache[key][:value]
109
201
  end
110
202
 
111
203
  def set_key(key, value)
112
- storage_contents = JSON.parse(File.read(Aromancer::Storage.storage_filepath))
113
- storage_contents[key.to_s] = value
204
+ key = key.to_s
205
+
206
+ # ensure default for key exists
207
+ @cache[key] = {value: nil, persisted: false} unless @cache.keys.include?(key)
208
+
209
+ # assign new cache value and set persisted to false
210
+ @cache[key][:value] = value
211
+ @cache[key][:persisted] = false
212
+
213
+ # return if storage open
214
+ # non-persisted cache values update next cycle
215
+ return if @storage_open
216
+
217
+ # open storage
218
+ @storage_open = true
219
+ storage_contents = {}
220
+ begin
221
+ # read storage
222
+ storage_contents = JSON.parse(File.read(Aromancer::Storage.storage_filepath))
223
+ rescue StandardError => e
224
+ @storage_open = false
225
+ FileUtils.rm(Aromancer::Storage.storage_filepath)
226
+ return
227
+ end
114
228
 
229
+ # update storage and cache
230
+ ([key] + @cache.keys.select{|k|
231
+ !@cache[k][:persisted]
232
+ }).each{|k|
233
+ storage_contents[k] = @cache[k][:value]
234
+ @cache[k][:persisted] = true
235
+ }
236
+
237
+ # write storage
115
238
  File.open(Aromancer::Storage.storage_filepath, "w+") do |f|
116
239
  f.write(storage_contents.to_json)
117
240
  end
241
+
242
+ # close storage
243
+ @storage_open = false
118
244
  end
119
245
  end
120
246
  end
@@ -11,5 +11,5 @@
11
11
  =end
12
12
 
13
13
  module Aromancer
14
- VERSION = "0.0.3"
14
+ VERSION = "0.0.5"
15
15
  end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ =begin
4
+
5
+ websocket.rb
6
+
7
+ websocket client.
8
+
9
+ by i2097i
10
+
11
+ =end
12
+
13
+ module Aromancer
14
+ class Websocket
15
+ include Singleton
16
+
17
+ TYPES = [
18
+ :arena,
19
+ :player
20
+ ]
21
+
22
+ attr_accessor :socket
23
+
24
+ def connect(api_key)
25
+ return unless self.socket.nil?
26
+ self.socket = WebSocket::Client::Simple.connect("#{Aromancer::Storage.get_websocket_url}?api_key=#{api_key}")
27
+ self.socket.on :message do |msg|
28
+ payload = JSON.parse(msg.data) rescue msg.data
29
+ if !payload.nil? &&
30
+ !payload["identifier"].nil? &&
31
+ !payload["message"].nil? &&
32
+ !payload["message"]["type"].nil? &&
33
+ TYPES.include?(payload["message"]["type"].to_sym)
34
+
35
+ legend_id = payload["message"]["id"]
36
+ lid = Aromancer::Storage.get_player&.symbolize_keys[:active_legend_id]
37
+ if !lid.nil? && lid == legend_id.to_i
38
+ Aromancer::Cli.instance.start_ui_thread
39
+ end
40
+ end
41
+ end
42
+
43
+ self.socket.on :open do
44
+ r = {
45
+ command: "subscribe",
46
+ identifier: {channel: "PlayerChannel", api_key: api_key}.to_json
47
+ }.to_json
48
+ Aromancer::Websocket.instance.socket.send(r)
49
+ end
50
+
51
+ self.socket.on :close do |e|
52
+ Aromancer::Websocket.instance.disconnect
53
+ end
54
+
55
+ self.socket.on :error do |e|
56
+ puts e.inspect
57
+ Aromancer::Websocket.instance.disconnect
58
+ end
59
+ end
60
+
61
+ def disconnect
62
+ self.socket = nil unless self.socket.nil?
63
+ end
64
+ end
65
+ end
data/lib/aromancer.rb CHANGED
@@ -14,8 +14,98 @@ require_relative :reiquire.to_s
14
14
  Reiquire::aromancer
15
15
 
16
16
  module Aromancer
17
+ THROTTLE_MS = 0.4
17
18
  FONT = TTY::Font.new(:standard)
18
19
 
20
+ # instance runs on main thread
21
+ class Cli
22
+ include Singleton
23
+
24
+ attr_accessor :ui_thread, :ws_thread, :busy, :last_render
25
+
26
+ # user entry point
27
+ def self.run
28
+ Thread.main[:route] = :authentication
29
+ self.instance.last_render = Time.now
30
+ Aromancer::Prompt.say(I18n.t("shared.welcome", version: Aromancer::VERSION))
31
+ Aromancer::Prompt.say(I18n.t("shared.server_url", url: Aromancer::Storage::get_server_url))
32
+ self.instance.start_ui_thread
33
+ self.instance.start_ws_thread
34
+ loop do
35
+ begin
36
+ self.instance.ui_thread.join
37
+ rescue NoMethodError => e
38
+ self.instance.start_ui_thread
39
+ sleep(1)
40
+ rescue Interrupt => e
41
+ exit
42
+ end
43
+ end
44
+ end
45
+
46
+ def start_ui_thread
47
+ if @busy || Time.now - @last_render < 2
48
+ # Thread.new{
49
+ # while Aromancer::Cli.instance.busy do
50
+ # if Time.now - Aromancer::Cli.instance.last_render > 5
51
+ # sleep(2)
52
+ # Aromancer::Cli.instance.start_ui_thread
53
+ # end
54
+ # end
55
+ # }.join
56
+ return
57
+ end
58
+ @busy = true
59
+ self.ui_thread&.exit
60
+ self.ui_thread&.kill
61
+ Thread.main[:input_controls] = nil
62
+ Aromancer::Prompt.instance.reload!
63
+ self.ui_thread = Thread.new do
64
+ loop do
65
+ ic = Thread.main[:input_controls]
66
+ unless ic.nil?
67
+ Aromancer::Routes.resume
68
+ sleep(THROTTLE_MS)
69
+ v = Aromancer::Prompt.p.select("", ic[:choices], show_help: :always, cycle: true, per_page: 11)
70
+ runners = ic[:runners].select{|r| r[:value] == v}
71
+ runners = ic[:runners].select{|r| r[:value].nil?} if runners.empty?
72
+
73
+ if runners.any?
74
+ r = runners.first
75
+ r[:proc].call(v)
76
+ else
77
+ exit
78
+ end
79
+ else
80
+ Aromancer::Routes.resume
81
+ end
82
+ Aromancer::Cli.instance.busy = false
83
+ Aromancer::Cli.instance.last_render = Time.now
84
+ # sleep(THROTTLE_MS)
85
+ end
86
+ end
87
+ end
88
+
89
+ def start_ws_thread
90
+ self.ws_thread.kill unless self.ws_thread.nil?
91
+ self.ws_thread = Thread.new do
92
+ loop do
93
+ player = Aromancer::Storage.get_player
94
+ if player.nil?
95
+ Aromancer::Websocket.instance.disconnect
96
+ elsif !player["api_key"].nil?
97
+ # attempt websocket connection
98
+ # puts :ws_thread
99
+ Aromancer::Websocket.instance.connect(player["api_key"])
100
+ end
101
+
102
+ sleep(4)
103
+ end
104
+ end
105
+ end
106
+
107
+ end
108
+
19
109
  def self.get_font(text = :aromancer.to_s, letter_spacing = 1)
20
110
  Aromancer::FONT.write(text, letter_spacing: letter_spacing).split("\n")
21
111
  end
data/lib/reiquire.rb CHANGED
@@ -13,7 +13,7 @@ require :"active_support/all".to_s
13
13
  require :i18n.to_s
14
14
  require :faraday.to_s
15
15
  require :fileutils.to_s
16
- # require :"websocket-client-simple".to_s
16
+ require :"websocket-client-simple".to_s
17
17
 
18
18
  # require :"tty-box".to_s # draw box shapes
19
19
  # require :"tty-color".to_s # detect color support
data/locale/en.yml CHANGED
@@ -7,7 +7,11 @@ en:
7
7
  inturrupt: "exiting due to user inturrupt..."
8
8
  reader_initialized: "input reader initialized."
9
9
  welcome: "welcome to aromancer. loading v%{version}..."
10
+ server_url: "current server: %{url}"
10
11
  navigate: "navigate to %{route}."
12
+ update_cancel: "update cancelled."
13
+ update_confirm: "confirm update [%{new_value}] (enter y): "
14
+ back: "back"
11
15
  exit: "exit"
12
16
  routes:
13
17
  authentication:
@@ -17,17 +21,28 @@ en:
17
21
  title: "authenticate"
18
22
  subtitle: ""
19
23
  main_menu:
24
+ configure: "configure"
20
25
  title: "main menu"
21
26
  subtitle: ""
22
27
  new_game: "new game"
23
28
  load_game: "load game"
29
+ player_count: "enter player count (1-2): "
24
30
  sign_out: "sign out"
25
31
  not_signed_in: "not signed in. invalid session. redirecting to authentication."
32
+ configure:
33
+ title: "configure %{name}"
34
+ new_host: "enter new server host: "
35
+ new_port: "enter new server port: "
36
+ force_ssl_toggle: "force ssl preference toggled to %{value}"
37
+ server_host: "host (%{stored})"
38
+ server_port: "port (%{stored})"
39
+ force_ssl: "force ssl (%{stored})"
26
40
  new_game:
27
41
  player_count: "number of players"
28
42
  story_count: "number of stories"
29
43
  starting: "starting new game..."
30
44
  status: "status"
45
+ cancelled: "cancelled. game not created."
31
46
  legend:
32
47
  title: "legend"
33
48
  subtitle: "%{subtitle}"
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.3
4
+ version: 0.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - i2097i
@@ -29,14 +29,14 @@ dependencies:
29
29
  requirements:
30
30
  - - "~>"
31
31
  - !ruby/object:Gem::Version
32
- version: 4.0.21
32
+ version: 4.0.22
33
33
  type: :development
34
34
  prerelease: false
35
35
  version_requirements: !ruby/object:Gem::Requirement
36
36
  requirements:
37
37
  - - "~>"
38
38
  - !ruby/object:Gem::Version
39
- version: 4.0.21
39
+ version: 4.0.22
40
40
  - !ruby/object:Gem::Dependency
41
41
  name: listen
42
42
  requirement: !ruby/object:Gem::Requirement
@@ -135,6 +135,20 @@ dependencies:
135
135
  - - "~>"
136
136
  - !ruby/object:Gem::Version
137
137
  version: 1.8.0
138
+ - !ruby/object:Gem::Dependency
139
+ name: websocket-client-simple
140
+ requirement: !ruby/object:Gem::Requirement
141
+ requirements:
142
+ - - "~>"
143
+ - !ruby/object:Gem::Version
144
+ version: 0.9.0
145
+ type: :runtime
146
+ prerelease: false
147
+ version_requirements: !ruby/object:Gem::Requirement
148
+ requirements:
149
+ - - "~>"
150
+ - !ruby/object:Gem::Version
151
+ version: 0.9.0
138
152
  - !ruby/object:Gem::Dependency
139
153
  name: tty-cursor
140
154
  requirement: !ruby/object:Gem::Requirement
@@ -229,6 +243,8 @@ files:
229
243
  - checksums/aromancer-0.0.1.gem.sha512
230
244
  - checksums/aromancer-0.0.2.gem.sha512
231
245
  - checksums/aromancer-0.0.3.gem.sha512
246
+ - checksums/aromancer-0.0.4.gem.sha512
247
+ - checksums/aromancer-0.0.5.gem.sha512
232
248
  - gem_graph.png
233
249
  - lib/aromancer.rb
234
250
  - lib/aromancer/.DS_Store
@@ -238,6 +254,7 @@ files:
238
254
  - lib/aromancer/server.rb
239
255
  - lib/aromancer/storage.rb
240
256
  - lib/aromancer/version.rb
257
+ - lib/aromancer/websocket.rb
241
258
  - lib/reiquire.rb
242
259
  - listen.rb
243
260
  - locale/en.yml