rest-gw2 0.4.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
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>