rest-gw2 0.4.0 → 0.5.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.
Files changed (61) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGES.md +30 -0
  3. data/README.md +7 -3
  4. data/Rakefile +1 -0
  5. data/TODO.md +26 -0
  6. data/config.ru +6 -0
  7. data/lib/rest-gw2.rb +1 -0
  8. data/lib/rest-gw2/client.rb +249 -110
  9. data/lib/rest-gw2/client/item_detail.rb +80 -0
  10. data/lib/rest-gw2/server.rb +5 -583
  11. data/lib/rest-gw2/server/action.rb +283 -0
  12. data/lib/rest-gw2/server/cache.rb +14 -4
  13. data/lib/rest-gw2/server/imp.rb +270 -0
  14. data/lib/rest-gw2/server/runner.rb +1 -0
  15. data/lib/rest-gw2/server/view.rb +309 -0
  16. data/lib/rest-gw2/{view → server/view}/characters.erb +10 -6
  17. data/lib/rest-gw2/server/view/check_list.erb +10 -0
  18. data/lib/rest-gw2/server/view/check_percentage.erb +9 -0
  19. data/lib/rest-gw2/server/view/commerce.erb +24 -0
  20. data/lib/rest-gw2/{view → server/view}/dyes.erb +7 -7
  21. data/lib/rest-gw2/server/view/error.erb +1 -0
  22. data/lib/rest-gw2/server/view/exchange.erb +29 -0
  23. data/lib/rest-gw2/server/view/guild_info.erb +37 -0
  24. data/lib/rest-gw2/{view → server/view}/index.erb +0 -0
  25. data/lib/rest-gw2/{view → server/view}/info.erb +1 -1
  26. data/lib/rest-gw2/{view → server/view}/item_list.erb +0 -0
  27. data/lib/rest-gw2/{view/items.erb → server/view/item_section.erb} +0 -0
  28. data/lib/rest-gw2/{view → server/view}/item_show.erb +7 -1
  29. data/lib/rest-gw2/server/view/items.erb +8 -0
  30. data/lib/rest-gw2/server/view/items_from.erb +35 -0
  31. data/lib/rest-gw2/{view → server/view}/layout.erb +14 -4
  32. data/lib/rest-gw2/server/view/members.erb +23 -0
  33. data/lib/rest-gw2/server/view/menu.erb +13 -0
  34. data/lib/rest-gw2/server/view/menu_armors.erb +17 -0
  35. data/lib/rest-gw2/server/view/menu_commerce.erb +7 -0
  36. data/lib/rest-gw2/server/view/menu_guild.erb +11 -0
  37. data/lib/rest-gw2/server/view/menu_unlocks.erb +11 -0
  38. data/lib/rest-gw2/{view → server/view}/menu_weapons.erb +2 -1
  39. data/lib/rest-gw2/{view → server/view}/pages.erb +2 -2
  40. data/lib/rest-gw2/{view → server/view}/profile.erb +7 -7
  41. data/lib/rest-gw2/server/view/skins.erb +15 -0
  42. data/lib/rest-gw2/server/view/stash.erb +10 -0
  43. data/lib/rest-gw2/server/view/titles.erb +5 -0
  44. data/lib/rest-gw2/server/view/unlock_percentage.erb +1 -0
  45. data/lib/rest-gw2/server/view/unlocks_items.erb +5 -0
  46. data/lib/rest-gw2/server/view/unlocks_list.erb +3 -0
  47. data/lib/rest-gw2/{view → server/view}/wallet.erb +1 -1
  48. data/lib/rest-gw2/server/view/wip.erb +1 -0
  49. data/lib/rest-gw2/version.rb +2 -1
  50. data/rest-gw2.gemspec +43 -25
  51. data/task/README.md +8 -8
  52. data/task/gemgem.rb +29 -7
  53. metadata +42 -25
  54. data/lib/rest-gw2/view/error.erb +0 -1
  55. data/lib/rest-gw2/view/guild.erb +0 -14
  56. data/lib/rest-gw2/view/items_from.erb +0 -30
  57. data/lib/rest-gw2/view/menu.erb +0 -16
  58. data/lib/rest-gw2/view/menu_armors.erb +0 -11
  59. data/lib/rest-gw2/view/skins.erb +0 -14
  60. data/lib/rest-gw2/view/transactions.erb +0 -28
  61. data/lib/rest-gw2/view/wip.erb +0 -1
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
 
2
3
  require 'rest-gw2'
3
4
 
@@ -0,0 +1,309 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rest-core'
4
+
5
+ require 'time'
6
+ require 'erb'
7
+ require 'cgi'
8
+ require 'zlib'
9
+
10
+ module RestGW2
11
+ class View < Struct.new(:request, :query_t)
12
+ HTML = Struct.new(:to_s)
13
+ COINS = %w[gold silver copper].zip(%w[
14
+ https://wiki.guildwars2.com/images/d/d1/Gold_coin.png
15
+ https://wiki.guildwars2.com/images/3/3c/Silver_coin.png
16
+ https://wiki.guildwars2.com/images/e/eb/Copper_coin.png
17
+ ]).freeze
18
+ GEM = 'https://wiki.guildwars2.com/images/a/aa/Gem.png'
19
+ FAVICONS = %w[
20
+ https://wiki.guildwars2.com/images/4/42/SAB_1_Bauble_Icon.png
21
+ https://wiki.guildwars2.com/images/9/96/SAB_5_Bauble_Icon.png
22
+ https://wiki.guildwars2.com/images/7/70/SAB_10_Bauble_Icon.png
23
+ https://wiki.guildwars2.com/images/9/93/SAB_20_Bauble_Icon.png
24
+ https://wiki.guildwars2.com/images/c/cd/SAB_50_Bauble_Icon.png
25
+ ]
26
+
27
+ def render name, arg=nil
28
+ erb(:layout){ erb(name, arg) }
29
+ end
30
+
31
+ # FIXME: controller shouldn't call this directly
32
+ def sum_trans trans
33
+ trans.inject(0) do |sum, t|
34
+ sum + t['price'] * t['quantity']
35
+ end
36
+ end
37
+
38
+ # FIXME: controller shouldn't call this directly
39
+ def sum_items items
40
+ items.inject([0, 0]) do |sum, i|
41
+ next sum unless i
42
+ b = i['buys']
43
+ s = i['sells']
44
+ sum[0] += b['unit_price'] * i['count'] if b
45
+ sum[1] += s['unit_price'] * i['count'] if s
46
+ sum
47
+ end
48
+ end
49
+
50
+ # FIXME: controller shouldn't call this directly
51
+ def show_guild g
52
+ HTML.new(menu("/guilds/#{g['id']}",
53
+ h("#{g['name']} [#{g['tag']}]")))
54
+ end
55
+
56
+ # FIXME: controller shouldn't call this directly
57
+ def query_p
58
+ @query_p ||= zero_is_nil(request.GET['p'])
59
+ end
60
+
61
+ # FIXME: controller shouldn't call this directly
62
+ def path str, q={}
63
+ RC::Middleware.request_uri(
64
+ RC::REQUEST_PATH => "#{ENV['RESTGW2_PREFIX']}#{str}",
65
+ RC::REQUEST_QUERY => q)
66
+ end
67
+
68
+ private
69
+ def erb name, arg=nil, &block
70
+ ERB.new(views(name)).result(binding, &block)
71
+ end
72
+
73
+ def favicon
74
+ FAVICONS[Zlib.crc32(query_t.to_s) % FAVICONS.size]
75
+ end
76
+
77
+ def h str
78
+ case str
79
+ when String
80
+ CGI.escape_html(str)
81
+ when HTML
82
+ str.to_s
83
+ end
84
+ end
85
+
86
+ def u str
87
+ CGI.escape(str) if str.kind_of?(String)
88
+ end
89
+
90
+ def views name
91
+ File.read("#{__dir__}/view/#{name}.erb")
92
+ end
93
+
94
+ def refresh_path
95
+ path(request.path, :p => query_p, :r => '1', :t => query_t)
96
+ end
97
+
98
+ # TODO: clean me up; can we not use block?
99
+ def menu item, name, query={}
100
+ href = path(item, query.merge(:t => query_t))
101
+ if path(request.path, :p => query_p, :t => query_t) == href
102
+ name
103
+ else
104
+ title = block_given? && yield
105
+ %Q{<a href="#{href}"#{title}>#{name}</a>}
106
+ end
107
+ end
108
+
109
+ # TODO: clean me up
110
+ def menu_sub prefix, item, name
111
+ key = "#{prefix}#{item}"
112
+ if path(request.path) == path(key)
113
+ menu(key, name, :p => query_p)
114
+ else
115
+ menu(key, name)
116
+ end
117
+ end
118
+
119
+ def menu_guild gid, item, name
120
+ menu("/guilds/#{gid}#{item}", name)
121
+ end
122
+
123
+ def menu_char name
124
+ menu("/characters/#{RC::Middleware.escape(name)}", name)
125
+ end
126
+
127
+ def menu_item item, name
128
+ menu_sub('/items', item, name)
129
+ end
130
+
131
+ def menu_unlock item, name
132
+ menu_sub('/unlocks', item, name)
133
+ end
134
+
135
+ def menu_skin item, name
136
+ menu_unlock("/skins#{item}", name)
137
+ end
138
+
139
+ def menu_commerce item, name
140
+ menu_sub('/commerce', item, name)
141
+ end
142
+
143
+ def page num
144
+ menu(request.path, num.to_s, :p => zero_is_nil(num))
145
+ end
146
+
147
+ # HELPER
148
+ def blank_icon
149
+ %Q{<img class="icon" alt="blank" src="https://upload.wikimedia.org/wikipedia/commons/d/d2/Blank.png"/>}
150
+ end
151
+
152
+ def item_wiki_list items
153
+ items.map(&method(:item_wiki)).join("\n")
154
+ end
155
+
156
+ def item_wiki item
157
+ if item['name'] && item['icon']
158
+ name = item['name'].tr(' ', '_')
159
+ img = %Q{<img class="#{item_class(item)}"} +
160
+ %Q{ alt="#{item_name(item)}"} +
161
+ %Q{ title="#{item_title(item)}"} +
162
+ %Q{ src="#{h item['icon']}"/>}
163
+ if name.empty?
164
+ img
165
+ else
166
+ %Q{<a href="http://wiki.guildwars2.com/wiki/#{u name}">#{img}</a>}
167
+ end
168
+ else
169
+ blank_icon
170
+ end
171
+ end
172
+
173
+ def item_link item
174
+ name = item_name(item)
175
+ type = item_type(item)
176
+ title = %Q{ title="#{type}"} if type
177
+
178
+ if item['nolink']
179
+ %Q{<span#{title}>#{name}</span>}
180
+ else
181
+ menu("/items/#{item['id']}", name) do
182
+ title
183
+ end
184
+ end
185
+ end
186
+
187
+ def item_name item
188
+ name = item['name'].to_s
189
+
190
+ h(!name.empty? && name || "?#{item['id']}?")
191
+ end
192
+
193
+ def item_type item
194
+ type = [item.dig('details', 'damage_type'),
195
+ item.dig('details', 'type')].join(' ')
196
+
197
+ !type.empty? && type || nil
198
+ end
199
+
200
+ def item_title item
201
+ d = item['description']
202
+ d && d.unpack('U*').map{ |c| "&##{c};" }.join
203
+ end
204
+
205
+ def item_count item
206
+ c = item['count']
207
+ "(#{c})" if c > 1
208
+ end
209
+
210
+ def item_price item
211
+ b = item['buys']
212
+ s = item['sells']
213
+ bb = b && price(b['unit_price'])
214
+ ss = s && price(s['unit_price'])
215
+ %Q{#{bb} / #{ss}#{item_total_price(item, b, s)}} if bb || ss
216
+ end
217
+
218
+ def item_total_price item, b, s
219
+ count = item['count']
220
+
221
+ if count > 1
222
+ bb = b && price(b['unit_price'] * count)
223
+ ss = s && price(s['unit_price'] * count)
224
+ " (#{bb} / #{ss})"
225
+ end
226
+ end
227
+
228
+ def item_class item
229
+ missing = if item['count'] == 0 then ' missing' end
230
+ rarity = if item['rarity']
231
+ " rarity rarity-#{item['rarity'].downcase}"
232
+ end
233
+ "icon#{rarity}#{missing}"
234
+ end
235
+
236
+ def item_attributes stats
237
+ stats.map do |name, value|
238
+ "#{name}: #{value}"
239
+ end.join(', ')
240
+ end
241
+
242
+ def price copper
243
+ g = copper / 100_00
244
+ s = copper % 100_00 / 100
245
+ c = copper % 100
246
+ l = [g, s, c]
247
+ n = l.index(&:nonzero?)
248
+ return '-' unless n
249
+ l.zip(COINS).drop(n).map do |(num, (name, src))|
250
+ price_tag(num, name, src)
251
+ end.join(' ')
252
+ end
253
+
254
+ def price_gem num
255
+ price_tag(num, 'gem', GEM)
256
+ end
257
+
258
+ def price_tag num, name, src
259
+ %Q{#{num}<img class="price"} +
260
+ %Q{ alt="#{name}" title="#{name}" src="#{src}"/>}
261
+ end
262
+
263
+ def dye_color dye
264
+ %w[cloth leather metal].map do |kind|
265
+ rgb = dye[kind]['rgb']
266
+ rgb && dye_preview(kind, rgb.join(', '))
267
+ end.join("\n")
268
+ end
269
+
270
+ def dye_preview kind, rgb
271
+ %Q{<span class="icon" title="#{kind}, rgb(#{rgb})"} +
272
+ %Q{ style="background-color: rgb(#{rgb})"></span>}
273
+ end
274
+
275
+ def abbr_time_ago time, precision=1
276
+ return unless time
277
+ ago = time_ago(time)
278
+ short = ago.take(precision).join(' ')
279
+ %Q{(<abbr title="#{time}, #{ago.join(' ')} ago">#{short} ago</abbr>)}
280
+ end
281
+
282
+ def time_ago time
283
+ duration((Time.now - Time.parse(time)).to_i)
284
+ end
285
+
286
+ def duration delta
287
+ result = []
288
+
289
+ [[ 60, :seconds],
290
+ [ 60, :minutes],
291
+ [ 24, :hours ],
292
+ [365, :days ],
293
+ [999, :years ]].
294
+ inject(delta) do |length, (divisor, name)|
295
+ quotient, remainder = length.divmod(divisor)
296
+ result.unshift("#{remainder} #{name}")
297
+ break if quotient == 0
298
+ quotient
299
+ end
300
+
301
+ result
302
+ end
303
+
304
+ def zero_is_nil n
305
+ r = n.to_i
306
+ r if r != 0
307
+ end
308
+ end
309
+ end
@@ -1,5 +1,5 @@
1
1
  Total Played:
2
- <%= duration(@total).join(' ') %> (<%= @total / 3600 %> hours)
2
+ <%= duration(arg[:total]).join(' ') %> (<%= arg[:total] / 3600 %> hours)
3
3
 
4
4
  <h2>Characters</h2>
5
5
  <table id="characters">
@@ -12,12 +12,16 @@ Total Played:
12
12
  <th>Death</th>
13
13
  <th>Birth</th>
14
14
  </tr>
15
- <% @chars.each do |c| %>
15
+ <% arg[:chars].each do |c| %>
16
16
  <tr>
17
17
  <td>
18
18
  <%= menu_char(c['name']) %> (<%= h c['level'].to_s %>)
19
- <br/>
20
- <%= show_guild(c['guild']) if c['guild'] %>
19
+ <% if c['guild'] %>
20
+ <br/><%= show_guild(c['guild']) %>
21
+ <% end %>
22
+ <% if c['title'] %>
23
+ <br/><%= c['title'] %>
24
+ <% end %>
21
25
  </td>
22
26
  <td><%= h c['profession'] %></td>
23
27
  <td><%= h "#{c['race']} #{c['gender']}" %></td>
@@ -32,10 +36,10 @@ Total Played:
32
36
 
33
37
  <h2>Crafting</h2>
34
38
  <table id="crafting">
35
- <% jellyfish.class.crafting.each do |d| %>
39
+ <% ServerAction.crafting.each do |d| %>
36
40
  <tr>
37
41
  <th><%= d %></th>
38
- <% @craftings[d].sort.reverse_each do |(rating, name, active)| %>
42
+ <% arg[:craftings][d].sort.reverse_each do |(rating, name, active)| %>
39
43
  <td class="active-<%= active %>"><%= "#{name} (#{rating})" %></td>
40
44
  <% end %>
41
45
  </tr>
@@ -0,0 +1,10 @@
1
+ <%= erb :check_percentage, arg %>
2
+
3
+ <ul>
4
+ <% arg[:list].each do |l| %>
5
+ <li>
6
+ <%= if l['count'] > 0 then '✔️' else '✖️' end %>
7
+ <%= l['name'] %>
8
+ </li>
9
+ <% end %>
10
+ </ul>
@@ -0,0 +1,9 @@
1
+ <% current = arg[:list].count{ |l| l['count'] > 0 } %>
2
+ <% total = arg[:list].size %>
3
+
4
+ <% if arg[:list].any? %>
5
+ <p>
6
+ <%= arg[:title] %>: <%= current %> / <%= total %>
7
+ (<%= ((current.to_f / total) * 100).round %>%)
8
+ </p>
9
+ <% end %>
@@ -0,0 +1,24 @@
1
+ <%= erb :menu_commerce %>
2
+ <%= erb :pages, arg[:pages] %>
3
+
4
+ Total: <%= price(arg[:total]) %>
5
+ <ul class="item">
6
+ <% arg[:items].each do |item| %>
7
+ <li>
8
+ <%= item_wiki(item) %>
9
+ <span class="description">
10
+ <%= item_link(item) %>
11
+ <%= h item_count(item) %>
12
+ <br/>
13
+ <% if item['price'] %>
14
+ <%= price(item['price']) %>
15
+ <%= abbr_time_ago(item['purchased'] || item['created']) %>
16
+ <br/>
17
+ <% end %>
18
+ <%= item_price(item) %>
19
+ </span>
20
+ </li>
21
+ <% end %>
22
+ </ul>
23
+
24
+ <%= erb :pages, arg[:pages] %>
@@ -1,11 +1,11 @@
1
- <% if @dyes.size > 0 %>
2
- Unlocked: <%= @unlocked %> / <%= @dyes.size %>
3
- (<%= ((@unlocked.to_f/@dyes.size)*100).to_i %>%)
4
- <% end %>
5
- <br/>
6
- Total: <%= price(@buy) %> / <%= price(@sell) %>
1
+ <%= erb :menu_unlocks %>
2
+
3
+ <%= erb :unlock_percentage, arg[:dyes] %>
4
+
5
+ Total: <%= price(arg[:buy]) %> / <%= price(arg[:sell]) %>
6
+
7
7
  <ul class="item">
8
- <% @dyes.each do |dye| %>
8
+ <% arg[:dyes].each do |dye| %>
9
9
  <li class="dye">
10
10
  <%= item_wiki(dye) %>
11
11
  <%= dye_color(dye) %>
@@ -0,0 +1 @@
1
+ <div class="error">Error: <%= h arg %></div>
@@ -0,0 +1,29 @@
1
+ <h2>Gem to Gold</h2>
2
+ <ul class="item">
3
+ <% arg[:buy_gold].each do |gold| %>
4
+ <li>
5
+ <%= item_wiki(gold) %>
6
+ <span class="description">
7
+ <%= h gold['name'] %>
8
+ (<%= price(gold['count']) %>)
9
+ <br/>
10
+ <%= price_gem(gold['price']) %>
11
+ </span>
12
+ </li>
13
+ <% end %>
14
+ </ul>
15
+
16
+ <h2>Gold to Gem</h2>
17
+ <ul class="item">
18
+ <% arg[:buy_gem].each do |gem| %>
19
+ <li>
20
+ <%= item_wiki(gem) %>
21
+ <span class="description">
22
+ <%= h gem['name'] %>
23
+ (<%= price_gem(gem['count']) %>)
24
+ <br/>
25
+ <%= price(gem['price']) %>
26
+ </span>
27
+ </li>
28
+ <% end %>
29
+ </ul>