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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 8beaa0b13cd7d66c054200ad4f919a7751e66a1a
4
- data.tar.gz: f48944e1a65d49072bc5bfdab62f739164978670
2
+ SHA256:
3
+ metadata.gz: 6977db172be53997d7f1f299d5961fce2a384d98cd85be0c815adc470437c434
4
+ data.tar.gz: dc9698c8f4d7c93c02dc13425169aaf7b25e8fc871ca56900b7d9ccda707bd25
5
5
  SHA512:
6
- metadata.gz: 06507ee1885c0a5c2ebfe1d050f56541ae5a5168552a527c46d22f5d281fb70ee56a37cec353c10cd6609acca2bc942e808acb58a748283f5470a76d08d3b9f5
7
- data.tar.gz: 4e5346ef0280fba26f7b9ed461a8fc60b2041a147ee4aca8310c095b2b821f60850df728b882e6966ae22507753bcec39092717bc1e5991a4d3293ec16d63de8
6
+ metadata.gz: 3035ba2ffb29c14d572488a582cfc7c5dcc0c4c64474b95bd8bb418703aa559825dac1b71c3c188c671b6b3cf16e3844d3a59574b5ef0f8964297ac4ac8e5365
7
+ data.tar.gz: fb4a98cb8fd9b2c85883af86f001080cee169e9e04eb5d3c72e2c192ea23ae3d70bf34a9dcc0073368ad2b5ff8883b9cdcc15a82a6ea676fad0c39b18204de78
data/CHANGES.md CHANGED
@@ -1,5 +1,35 @@
1
1
  # CHANGES
2
2
 
3
+ ## rest-gw2 0.5.0 -- 2018-12-26
4
+
5
+ * Bunch of performance improvement.
6
+ * Allow setting `ENV['GW2_POOL_SIZE']` to configure pool size.
7
+ * Added `RestGW2::Client#me`
8
+ * Added `RestGW2::Client#guild_members`
9
+ * Added `RestGW2::Client#outfits_with_detail`
10
+ * Added `RestGW2::Client#mailcarriers_with_detail`
11
+ * Added `RestGW2::Client#gliders_with_detail`
12
+ * Added `RestGW2::Client#all_outfits`
13
+ * Added `RestGW2::Client#all_mailcarriers`
14
+ * Added `RestGW2::Client#all_gliders`
15
+ * Added `RestGW2::Client#all_fininshers`
16
+ * Added `RestGW2::Client#all_cats`
17
+ * Added `RestGW2::Client#all_titles`
18
+ * Added `RestGW2::Client#finishers_with_detail`
19
+ * Added `RestGW2::Client#cats_with_detail`
20
+ * Added `RestGW2::Client#nodes_with_detail`
21
+ * Added `RestGW2::Client#titles_with_detail`
22
+ * Added `RestGW2::Client#delivery_with_detail`
23
+ * Added `RestGW2::Client#all_unlocks`
24
+ * Added `RestGW2::Client#unlocks_with_detail`
25
+ * Added `RestGW2::ItemDetail`
26
+ * Show guild_leader for account_with_detail
27
+ * Added a bunch of different unlocks pages
28
+ * Added exchange rate page
29
+ * Show favicon based on the access token
30
+ * Show rarity border for items
31
+ * Show total prices for a particular item
32
+
3
33
  ## rest-gw2 0.4.0 -- 2016-02-05
4
34
 
5
35
  * Added `RestGW2::Client#stash_with_detail`
data/README.md CHANGED
@@ -14,7 +14,7 @@ A very simple [Guild Wars 2 API](https://wiki.guildwars2.com/wiki/API:Main)
14
14
  client built with [rest-core](https://github.com/godfat/rest-core).
15
15
 
16
16
  There's also a bundled web application showing your items, serving as an
17
- example using the client.
17
+ example using the client. There's also a [demo site](https://gw2.godfat.org/)
18
18
 
19
19
  ## FEATURES:
20
20
 
@@ -23,7 +23,7 @@ example using the client.
23
23
 
24
24
  ## REQUIREMENTS:
25
25
 
26
- * Tested with MRI (official CRuby), Rubinius and JRuby.
26
+ * Tested with MRI (official CRuby) and JRuby.
27
27
  * rest-core
28
28
 
29
29
  ### REQUIREMENTS: (if you need the web application)
@@ -94,6 +94,10 @@ The format for the config file would be like:
94
94
  jruby -S rake server
95
95
  jruby -S rake console
96
96
 
97
+ ## Current production script:
98
+
99
+ jruby -Xcompile.invokedynamic=true -J-Xmx1536m -S rake server -- -p PORT -s trinidad
100
+
97
101
  ## CONTRIBUTORS:
98
102
 
99
103
  * Lin Jen-Shin (@godfat)
@@ -102,7 +106,7 @@ The format for the config file would be like:
102
106
 
103
107
  Apache License 2.0
104
108
 
105
- Copyright (c) 2015-2016, Lin Jen-Shin (godfat)
109
+ Copyright (c) 2015-2019, Lin Jen-Shin (godfat)
106
110
 
107
111
  Licensed under the Apache License, Version 2.0 (the "License");
108
112
  you may not use this file except in compliance with the License.
data/Rakefile CHANGED
@@ -21,6 +21,7 @@ desc 'Run server'
21
21
  task 'server' do
22
22
  ARGV.shift
23
23
  load 'bin/rest-gw2'
24
+ Rake.application.top_level_tasks.clear
24
25
  end
25
26
 
26
27
  Gemgem.init(dir) do |s|
data/TODO.md ADDED
@@ -0,0 +1,26 @@
1
+ # Test
2
+
3
+ * Write a Rack app crawler and visit all the pages, and save the responses
4
+ and the cache data from the client. We could have a test to restore the
5
+ cache data for the client, and then visit all the pages again, and compare
6
+ the saved responses and the new responses. They should be identical for
7
+ all (200 OK) pages, of course.
8
+
9
+ # View
10
+
11
+ * Put upgrades and infusions icons in a column, rather than a row.
12
+ * Show upgrades and infusions names and prices.
13
+
14
+ # Feature
15
+
16
+ * https://wiki.guildwars2.com/wiki/API:2/account/recipes
17
+ * https://wiki.guildwars2.com/wiki/API:2/account/masteries
18
+ * https://wiki.guildwars2.com/wiki/API:2/commerce/listings
19
+
20
+ # Code Clarity
21
+
22
+ * Use more classes and objects so that the types are more clear
23
+
24
+ # Performance
25
+
26
+ * Smart item details cache, basing on item id
data/config.ru CHANGED
@@ -1,8 +1,14 @@
1
1
 
2
2
  require 'rest-gw2'
3
+
3
4
  warmup do
4
5
  RestCore.eagerload
5
6
  RestCore.eagerload(RestGW2)
6
7
  RestGW2::Client.new
7
8
  end
9
+
10
+ if pool_size = ENV['GW2_POOL_SIZE']
11
+ RestGW2::Client.pool_size = Integer(pool_size)
12
+ end
13
+
8
14
  run RestGW2::Server
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
 
2
3
  module RestGW2
3
4
  autoload :Client, 'rest-gw2/client'
@@ -1,5 +1,8 @@
1
+ # frozen_string_literal: true
1
2
 
2
3
  require 'rest-core'
4
+ require 'rest-gw2/client/item_detail'
5
+ require 'set'
3
6
 
4
7
  module RestGW2
5
8
  Client = RC::Builder.client do
@@ -55,14 +58,25 @@ module RestGW2
55
58
  end
56
59
 
57
60
  Client.include(Module.new{
61
+ def me opts={}
62
+ get('v2/account', {}, opts)
63
+ end
64
+
58
65
  # https://wiki.guildwars2.com/wiki/API:2/account
59
66
  # https://wiki.guildwars2.com/wiki/API:2/worlds
60
- # https://wiki.guildwars2.com/wiki/API:1/guild_details
67
+ # https://wiki.guildwars2.com/wiki/API:2/guild/:id
61
68
  def account_with_detail opts={}
62
- me = get('v2/account', {}, opts)
63
- worlds = get('v2/worlds', :ids => me['world'])
64
- guilds = guilds_detail(me['guilds'])
65
- me.merge('world' => world_detail(worlds.first), 'guilds' => guilds)
69
+ m = me(opts)
70
+ worlds = get('v2/worlds', :ids => m['world'])
71
+ guilds = guilds_detail(m['guilds'])
72
+ # m['guild_leader'] would be nil if there's no guild permission
73
+ guild_leader = (m['guild_leader'] || []).map do |gid|
74
+ guilds.find{ |g| g['id'] == gid }
75
+ end
76
+ me.merge(
77
+ 'world' => world_detail(worlds.first),
78
+ 'guilds' => guilds,
79
+ 'guild_leader' => guild_leader)
66
80
  end
67
81
 
68
82
  # https://wiki.guildwars2.com/wiki/API:2/guild/:id/stash
@@ -86,6 +100,11 @@ module RestGW2
86
100
  end
87
101
  end
88
102
 
103
+ # https://wiki.guildwars2.com/wiki/API:2/guild/:id/members
104
+ def guild_members gid, opts={}
105
+ get("v2/guild/#{gid}/members", {}, opts)
106
+ end
107
+
89
108
  # https://wiki.guildwars2.com/wiki/API:2/characters
90
109
  def get_character name, opts={}
91
110
  get("v2/characters/#{RC::Middleware.escape(name)}", {}, opts)
@@ -97,12 +116,18 @@ module RestGW2
97
116
  get_character(name, opts)
98
117
  end
99
118
 
100
- guilds = chars.map do |c|
101
- get_guild(c['guild']) if c['guild']
102
- end
119
+ guild_ids = chars.map{ |c| c['guild'] }.compact.uniq
120
+ guild_promises = guilds_detail(guild_ids)
121
+
122
+ title_ids = chars.map{ |c| c['title'] }.compact
123
+ titles = get('v2/titles', :ids => title_ids.join(',')).
124
+ group_by{ |t| t['id'] }
125
+
126
+ guilds = guild_promises.group_by{ |g| g['id'] }
103
127
 
104
- chars.zip(guilds).map do |(c, g)|
105
- c['guild'] = g
128
+ chars.map do |c|
129
+ c['guild'] = guilds.dig(c['guild'], 0)
130
+ c['title'] = titles.dig(c['title'], 0, 'name')
106
131
  c
107
132
  end.sort_by{ |c| -c['age'] }
108
133
  end
@@ -115,81 +140,195 @@ module RestGW2
115
140
  end
116
141
  end
117
142
 
143
+ # https://wiki.guildwars2.com/wiki/API:2/currencies
144
+ # https://wiki.guildwars2.com/wiki/API:2/account/wallet
145
+ def wallet_with_detail opts={}
146
+ wallet = get('v2/account/wallet', {}, opts)
147
+ ids = wallet.map{ |w| w['id'] }.join(',')
148
+ currencies = get('v2/currencies', :ids => ids).group_by{ |w| w['id'] }
149
+ wallet.map do |currency|
150
+ currency.merge(currencies[currency['id']].first)
151
+ end.sort_by{ |c| c['order'] }
152
+ end
153
+
154
+ # https://wiki.guildwars2.com/wiki/API:2/account/skins
155
+ def skins_with_detail opts={}
156
+ unlocks_with_detail(:all_skins, 'v2/account/skins', opts)
157
+ end
158
+
159
+ # https://wiki.guildwars2.com/wiki/API:2/account/outfits
160
+ def outfits_with_detail opts={}
161
+ unlocks_with_detail(:all_outfits, 'v2/account/outfits', opts)
162
+ end
163
+
164
+ # https://wiki.guildwars2.com/wiki/API:2/account/mailcarriers
165
+ def mailcarriers_with_detail opts={}
166
+ unlocks_with_detail(:all_mailcarriers, 'v2/account/mailcarriers', opts)
167
+ end
168
+
169
+ # https://wiki.guildwars2.com/wiki/API:2/account/gliders
170
+ def gliders_with_detail opts={}
171
+ unlocks_with_detail(:all_gliders, 'v2/account/gliders', opts)
172
+ end
173
+
174
+ # https://wiki.guildwars2.com/wiki/API:2/skins
175
+ def all_skins
176
+ all_unlocks('v2/skins')
177
+ end
178
+
179
+ # https://wiki.guildwars2.com/wiki/API:2/outfits
180
+ def all_outfits
181
+ all_unlocks('v2/outfits')
182
+ end
183
+
184
+ # https://wiki.guildwars2.com/wiki/API:2/mailcarriers
185
+ def all_mailcarriers
186
+ all_unlocks('v2/mailcarriers')
187
+ end
188
+
189
+ # https://wiki.guildwars2.com/wiki/API:2/gliders
190
+ def all_gliders
191
+ all_unlocks('v2/gliders')
192
+ end
193
+
194
+ # https://wiki.guildwars2.com/wiki/API:2/finishers
195
+ def all_fininshers
196
+ all_unlocks('v2/finishers')
197
+ end
198
+
199
+ # https://wiki.guildwars2.com/wiki/API:2/cats
200
+ def all_cats
201
+ all_unlocks('v2/cats')
202
+ end
203
+
204
+ # https://wiki.guildwars2.com/wiki/API:2/titles
205
+ def all_titles
206
+ all_unlocks('v2/titles')
207
+ end
208
+
118
209
  # https://wiki.guildwars2.com/wiki/API:2/colors
119
210
  # https://wiki.guildwars2.com/wiki/API:2/account/dyes
120
211
  def dyes_with_detail opts={}
121
212
  mine = get('v2/account/dyes', opts)
213
+
122
214
  get('v2/colors').each_slice(100).map do |slice|
123
- slice.join(',')
124
- end.map do |ids|
125
- with_item_detail('v2/colors', :ids => ids) do |colors|
215
+ with_item_detail('v2/colors', :ids => slice.join(',')) do |colors|
126
216
  colors.map{ |c| c.merge('id' => c['item'], 'color_id' => c['id']) }
127
217
  end
128
218
  end.flatten.map do |color|
129
- color['count'] = if mine.include?(color['color_id'])
130
- 1
131
- else
132
- 0
133
- end
219
+ color['count'] =
220
+ if mine.include?(color['color_id'])
221
+ 1
222
+ else
223
+ 0
224
+ end
134
225
  color
135
226
  end.sort_by{ |c| c['categories'] }
136
227
  end
137
228
 
138
- # https://wiki.guildwars2.com/wiki/API:2/account/skins
139
- def skins_with_detail opts={}
140
- mine = get('v2/account/skins', {}, opts)
141
- all_skins.flatten.map do |skin|
142
- skin['count'] = if mine.include?(skin['id'])
143
- 1
144
- else
145
- 0
146
- end
147
- skin['nolink'] = true
148
- skin
149
- end.sort_by{ |s| s['name'] || '' }
150
- end
151
-
152
- # https://wiki.guildwars2.com/wiki/API:2/skins
153
- def all_skins
154
- get('v2/skins').each_slice(100).map do |slice|
155
- get('v2/skins', :ids => slice.join(','))
156
- end
157
- end
158
-
159
229
  # https://wiki.guildwars2.com/wiki/API:2/minis
160
230
  # https://wiki.guildwars2.com/wiki/API:2/account/minis
161
231
  def minis_with_detail opts={}
162
232
  mine = get('v2/account/minis', {}, opts)
233
+
163
234
  get('v2/minis').each_slice(100).map do |slice|
164
- slice.join(',')
165
- end.map do |ids|
166
- with_item_detail('v2/minis', :ids => ids) do |minis|
235
+ with_item_detail('v2/minis', :ids => slice.join(',')) do |minis|
167
236
  minis.map{ |m| m.merge('id' => m['item_id'], 'mini_id' => m['id']) }
168
237
  end
169
238
  end.flatten.map do |mini|
170
- mini['count'] = if mine.include?(mini['mini_id'])
171
- 1
172
- else
173
- 0
174
- end
239
+ mini['count'] =
240
+ if mine.include?(mini['mini_id'])
241
+ 1
242
+ else
243
+ 0
244
+ end
245
+ mini['nolink'] = true
175
246
  mini
176
247
  end.sort_by{ |m| m['order'] }
177
248
  end
178
249
 
179
- # https://wiki.guildwars2.com/wiki/API:2/account/wallet
180
- # https://wiki.guildwars2.com/wiki/API:2/currencies
181
- def wallet_with_detail opts={}
182
- wallet = get('v2/account/wallet', {}, opts)
183
- ids = wallet.map{ |w| w['id'] }.join(',')
184
- currencies = get('v2/currencies', :ids => ids).group_by{ |w| w['id'] }
185
- wallet.map do |currency|
186
- currency.merge(currencies[currency['id']].first)
187
- end.sort_by{ |c| c['order'] }
250
+ # https://wiki.guildwars2.com/wiki/API:2/account/finishers
251
+ def finishers_with_detail opts={}
252
+ mine_promise = get('v2/account/finishers', {}, opts)
253
+
254
+ all = all_fininshers
255
+ mine = mine_promise.group_by{ |u| u['id'] }
256
+
257
+ all.flatten.map do |finisher|
258
+ finisher['count'] =
259
+ if mine.dig(finisher['id'], 0, 'permanent')
260
+ Float::INFINITY
261
+ else
262
+ mine.dig(finisher['id'], 0, 'quantity') || 0
263
+ end
264
+ finisher['nolink'] = true
265
+ finisher['description'] = finisher['unlock_details']
266
+ finisher
267
+ end.sort_by{ |m| m['order'] }
188
268
  end
189
269
 
190
- def with_item_detail path, query={}, opts={}, &block
191
- block ||= :itself.to_proc
192
- expand_item_detail(block.call(get(path, query, opts)), opts)
270
+ # https://wiki.guildwars2.com/wiki/API:2/account/home/cats
271
+ def cats_with_detail opts={}
272
+ mine_promise = get('v2/account/home/cats', opts)
273
+
274
+ all = all_cats
275
+ mine = Set.new(mine_promise.map{ |cat| cat['id'] })
276
+
277
+ all.flatten.map do |cat|
278
+ cat['count'] =
279
+ if mine.member?(cat['id'])
280
+ 1
281
+ else
282
+ 0
283
+ end
284
+ cat['name'] = cat['hint']
285
+ cat
286
+ end.sort_by{ |c| c['name'] }
287
+ end
288
+
289
+ # https://wiki.guildwars2.com/wiki/API:2/nodes
290
+ # https://wiki.guildwars2.com/wiki/API:2/account/home/nodes
291
+ def nodes_with_detail opts={}
292
+ all = get('v2/nodes')
293
+ mine = Set.new(get('v2/account/home/nodes', opts))
294
+
295
+ all.map do |name|
296
+ count =
297
+ if mine.member?(name)
298
+ 1
299
+ else
300
+ 0
301
+ end
302
+ {'name' => name, 'count' => count}
303
+ end.sort_by{ |n| n['name'] }
304
+ end
305
+
306
+ # https://wiki.guildwars2.com/wiki/API:2/account/titles
307
+ def titles_with_detail opts={}
308
+ all = all_titles
309
+ mine = get('v2/account/titles')
310
+
311
+ all.flatten.map do |title|
312
+ title['count'] =
313
+ if mine.member?(title['id'])
314
+ 1
315
+ else
316
+ 0
317
+ end
318
+ title
319
+ end.sort_by{ |t| t['name'] }
320
+ end
321
+
322
+ # https://wiki.guildwars2.com/wiki/API:2/commerce/delivery
323
+ def delivery_with_detail query={}, opts={}
324
+ items = with_item_detail('v2/commerce/delivery',
325
+ {:page_size => 200}.merge(query), opts) do |delivery|
326
+ ['price' => delivery['coins']] + delivery['items']
327
+ end
328
+
329
+ compact_items(items) do |last, current|
330
+ last['id'] == current['id']
331
+ end
193
332
  end
194
333
 
195
334
  # https://wiki.guildwars2.com/wiki/API:2/commerce/transactions
@@ -203,62 +342,26 @@ module RestGW2
203
342
  end
204
343
 
205
344
  def transactions_with_detail_compact path, query={}, opts={}
206
- transactions_with_detail(path, query, opts).inject([]) do |ret, trans|
207
- last = ret.last
208
- if last && last['item_id'] == trans['item_id'] &&
209
- last['price'] == trans['price']
210
- last['count'] += trans['count']
211
- else
212
- ret << trans
213
- end
214
- ret
345
+ items = transactions_with_detail(path, query, opts)
346
+
347
+ compact_items(items) do |last, current|
348
+ last['id'] == current['id'] && last['price'] == current['price']
215
349
  end
216
350
  end
217
351
 
218
- # https://wiki.guildwars2.com/wiki/API:2/items
219
- # https://wiki.guildwars2.com/wiki/API:2/commerce/prices
352
+ def with_item_detail path, query={}, opts={}, &block
353
+ block ||= :itself.to_proc
354
+ expand_item_detail(block.call(get(path, query, opts)), opts)
355
+ end
356
+
220
357
  def expand_item_detail items, opts={}
221
- skins = all_skins
222
- detail = item_detail_group_by_id(items, opts)
223
- upgrades = extract_items_in_slots(items, opts, 'upgrades', 'infusions')
224
-
225
- skins_detail = skins.flatten.group_by{ |s| s['id'] }
226
- items.map do |i|
227
- next i unless data = i && detail[i['id']]
228
- s = i['skin']
229
- u = i['upgrades']
230
- f = i['infusions']
231
- i.merge(data).merge(
232
- 'count' => i['count'] || 1,
233
- 'skin' => s && skins_detail[s].first,
234
- 'upgrades' => u && u.flat_map(&upgrades.method(:[])),
235
- 'infusions' => f && f.flat_map(&upgrades.method(:[])))
236
- end
358
+ detail = ItemDetail.new(self, items, opts)
359
+ detail.populate
360
+
361
+ items.map(&detail.method(:fill))
237
362
  end
238
363
 
239
364
  private
240
- def item_detail_group_by_id items, opts={}
241
- items.map{ |i| i && i['id'] }.compact.each_slice(100).map do |slice|
242
- q = {:ids => slice.join(',')}
243
- [get('v2/items', q),
244
- get('v2/commerce/prices', q, {:error_detector => false}.merge(opts))]
245
- end.flat_map(&:itself).map(&:to_a).flatten.group_by{ |i| i['id'] }.
246
- inject({}){ |r, (id, v)| r[id] = v.inject(&:merge); r }
247
- # this is probably a dirty way to workaround converting hashes to arrays
248
- end
249
-
250
- def extract_items_in_slots items, opts, *slots
251
- upgrades = items.flat_map do |i|
252
- if i
253
- i.values_at(*slots).flatten.compact.map do |id|
254
- {'id' => id}
255
- end
256
- else
257
- []
258
- end
259
- end
260
- item_detail_group_by_id(upgrades, opts)
261
- end
262
365
 
263
366
  # https://wiki.guildwars2.com/wiki/API:2/worlds
264
367
  def world_detail world
@@ -285,13 +388,49 @@ module RestGW2
285
388
  "#{world['name']} (#{world['population']}) / #{region} (#{lang})"
286
389
  end
287
390
 
288
- # https://wiki.guildwars2.com/wiki/API:1/guild_details
289
391
  def guilds_detail guilds
290
392
  guilds.map(&method(:get_guild))
291
393
  end
292
394
 
395
+ # https://wiki.guildwars2.com/wiki/API:2/guild/:id
293
396
  def get_guild gid
294
- get('v1/guild_details', :guild_id => gid)
397
+ get("v2/guild/#{gid}")
398
+ end
399
+
400
+ # Returns Array[Promise[Detail]]
401
+ def all_unlocks path
402
+ get(path).each_slice(100).map do |slice|
403
+ get(path, :ids => slice.join(','))
404
+ end
405
+ end
406
+
407
+ def unlocks_with_detail kind, path, opts
408
+ all = public_send(kind)
409
+ mine = Set.new(get(path, {}, opts))
410
+ all.flatten.map do |unlock|
411
+ unlock['count'] =
412
+ if mine.member?(unlock['id'])
413
+ 1
414
+ else
415
+ 0
416
+ end
417
+ unlock['nolink'] = true
418
+ unlock
419
+ end.sort_by{ |u| u['order'] || u['name'] || '' }
420
+ end
421
+
422
+ def compact_items items
423
+ items.inject([]) do |result, item|
424
+ last = result.last
425
+
426
+ if last && yield(last, item)
427
+ last['count'] += item['count']
428
+ else
429
+ result << item
430
+ end
431
+
432
+ result
433
+ end
295
434
  end
296
435
  })
297
436
  end