poe-watch-api 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 8bcf552167fc4cc9a0b3945ebd661e7830f3d39da65ba739a34b9dcc62f953d3
4
+ data.tar.gz: 0fb40c82867df71538b03e187bc09fa4b62f43007edf39d022b601c25279f434
5
+ SHA512:
6
+ metadata.gz: 4c6624a5ffc9c2ea4a5ab717121e4fdc20d48bcb40d4ba5eb42996f70c314b3b331a71ac1df9b9f88d34c5a67e0dceda0d543cc36c0148003b0f2f3bc2a6e03c
7
+ data.tar.gz: fae8e08f03d03d1f0e2859e9d1963144df65fe5108b1ba3e0c6715328717974669dece5baa8882862d04be74a132abc6e6e1c62064d62a97eae411b1e0587cc7
data/LICENSE ADDED
@@ -0,0 +1,13 @@
1
+ DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
2
+ Version 2, December 2004
3
+
4
+ Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
5
+
6
+ Everyone is permitted to copy and distribute verbatim or modified
7
+ copies of this license document, and changing it is allowed as long
8
+ as the name is changed.
9
+
10
+ DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
11
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
12
+
13
+ 0. You just DO WHAT THE FUCK YOU WANT TO.
@@ -0,0 +1,310 @@
1
+ # Poe Watch Ruby API
2
+
3
+ A Ruby wrapper around the [poe.watch API](https://poe.watch/api). It implements the same DSL you are used to when using `ActiveRecord` but doesn't require Rails at all.
4
+
5
+ Inspired by [klayveR's poe-watch-api JS wrapper](https://github.com/klayveR/poe-watch-api).
6
+
7
+ ## Table of content
8
+
9
+ - [Requirements](#requirements)
10
+ - [Installation](#installation)
11
+ - [Dependencies](#dependencies)
12
+ - [Usage](#usage)
13
+ * [Items](#items)
14
+ + [Get all item data](#get-all-item-data)
15
+ + [Get multiple specific items](#get-multiple-specific-items)
16
+ + [Find one specific item](#find-one-specific-item)
17
+ + [Item properties](#item-properties)
18
+ * [Item price data](#item-price-data)
19
+ + [Price data properties](#price-data-properties)
20
+ * [Leagues](#leagues)
21
+ + [Get all league data](#get-all-league-data)
22
+ + [Get multiple specific leagues](#get-multiple-specific-leagues)
23
+ + [Find one specific league](#find-one-specific-league)
24
+ + [League properties](#league-properties)
25
+ * [Categories](#categories)
26
+ + [Get all categories data](#get-all-categories-data)
27
+ + [Get multiple specific categories](#get-multiple-specific-categories)
28
+ + [Find one specific category](#find-one-specific-category)
29
+ + [Category properties](#category-properties)
30
+ + [API Cache](#api-cache)
31
+ - [Shortcomings](#shortcomings)
32
+ - [Contribution](#contribution)
33
+ - [License](#license)
34
+
35
+
36
+ ## Requirements
37
+
38
+ - Redis: 4.0+
39
+ - Ruby 2.3+
40
+
41
+ ## Installation
42
+
43
+ ```
44
+ gem install poe-watch-api
45
+ ```
46
+
47
+ or add it to your `Gemfile`
48
+
49
+ ```
50
+ gem 'poe-watch-api'
51
+ ```
52
+
53
+ ## Dependencies
54
+
55
+ This library has a dependency over `redis`. For it to work you need to have redis installed on your machine (and your server, in production, if using Heroku, you can use an addon like `rediscloud`).
56
+ We use `redis` because we need to cache the data from Poe Watch to avoid refetching all the items data to often (there are almost 30k items in PoE which is quite a lot).
57
+
58
+ There are two ways of making this gem work with redis:
59
+
60
+ - Set a global `$redis` variable.
61
+ Example:
62
+ ```ruby
63
+ $redis = Redis.new
64
+
65
+ # In production for instance if you were to use rediscloud on heroku :
66
+ url = ENV["REDISCLOUD_URL"] # or whatever is the URL of your redis
67
+
68
+ if url
69
+ $redis = Redis.new(:url => url)
70
+ end
71
+ ```
72
+
73
+ - Provide your own instance of redis through `PoeWatch::Api.redis = Redis.new(YOUR_PARAMS)`
74
+ Example:
75
+ ```ruby
76
+ PoeWatch::Api.redis = Redis.new
77
+
78
+ # In production for instance, if you were to use rediscloud on heroku:
79
+ url = ENV["REDISCLOUD_URL"] # or whatever is the URL of your redis
80
+
81
+ if url
82
+ PoeWatch::Api.redis = Redis.new(:url => url)
83
+ end
84
+ ```
85
+
86
+ The total footprint of the data from PoeWatch API in redis is a bit less than **2 megabytes**. You can check the exact memory footprint in your redis by calling `PoeWatch::Api.memory_footprint`, the data will be displayed in kilobytes.
87
+ The default time to live (TTL) of the cache is 45 minutes.
88
+
89
+ ## Usage
90
+
91
+ Here is a simple usage example:
92
+ ```
93
+ # See the dependencies section
94
+ PoeWatch::Api.redis = Redis.new
95
+
96
+ # Non mandatory
97
+ PoeWatch::Api.refresh!(3600) # cache for 1 hour
98
+
99
+ PoeWatch::Item.find({ name: "Circle of Nostalgia }).price_for_league('Metamorph')
100
+ ```
101
+
102
+ ### Items
103
+
104
+ #### Get all item data
105
+
106
+ ```ruby
107
+ PoeWatch::Item.all # => array of PoeWatch::Item objects
108
+ PoeWatch::Item.count # => total number of items
109
+ ```
110
+
111
+ #### Get multiple specific items
112
+ ```ruby
113
+ items = PoeWatch::Item.where(name: /circle/i) # Returns all items with a name containing "circle"
114
+
115
+ items.first.id # => 223
116
+ items.first.name # => "Hubris Circlet"
117
+ ```
118
+
119
+ #### Find one specific item
120
+ ```ruby
121
+ item = PoeWatch::Item.find(name: "Circle of Nostalgia") # Returns the item "Circle of Nostalgia"
122
+ item = PoeWatch::Item.find(name: /circle/i) # Returns the first item with a name containing "circle"
123
+
124
+ items.id # => 223
125
+ items.name # => "Hubris Circlet"
126
+ ```
127
+
128
+ #### Item properties
129
+
130
+ All properties have an accessor you can use, e.g: `item.id`, `item.name`, `item.hardcore`.
131
+ All boolean properties have an additionnal question mark accessor, e.g: `item.hardcore?`.
132
+
133
+ Items only have relevant properties (e.g: map tiers only on maps, stack_size only on currencies, etc...)
134
+
135
+ | Name | Type | Description |
136
+ | ----------- | --------- | ------------ |
137
+ | id | `Integer` | poe.watch ID |
138
+ | name | `String` | name |
139
+ | type | `String` | base type |
140
+ | category | `String` | item category |
141
+ | group | `String` | item category group |
142
+ | frame | `Integer` | frame type (item rarity) |
143
+ | map_series | `Integer` | 5 for Synthesis, 1 for Awakening, etc |
144
+ | map_tier | `Integer` | map tier |
145
+ | gem_level | `Integer` | gem level |
146
+ | gem_quality | `Integer` | gem quality |
147
+ | gem_is_corrupted | `Boolean` | is the gem corrupted |
148
+ | link_count | `Integer` | links count |
149
+ | base_item_level | `Integer` | item level |
150
+ | variation | `String` | variations of the same item (e.g: Vessel of Vinktar) |
151
+ | enchant_min | `Integer` | enchantment's minimum has numeric value
152
+ | enchant_max | `Integer` | enchantment's maximum has numeric value
153
+ | relic | `Integer` | whether the item is a relic or not. `true` always sets `properties.frame` to `9` |
154
+ | icon | `String` | icon URL |
155
+ | influences | `Array` | shaper / elder / conqueror influences |
156
+ | stack_size | `Integer` | default stack size of item type |
157
+
158
+
159
+ ### Item price data
160
+
161
+ ```ruby
162
+ item = PoeWatch::Item.find(name: "Circle of Nostalgia")
163
+
164
+ # Returns price data for all leagues
165
+ item.prices
166
+
167
+ # Returns price data for all leagues matching the name
168
+ item.price_for_leagues("Metamorph") # => all metamorph leagues (HC / SC) price data
169
+
170
+ # Returns price data for a specific league
171
+ item.price_for_leagues("Metamorph") # => Price data for "Metamorph"
172
+ item.price_for_leagues(/metamorph/i) # => Price data for the first metamorph league found
173
+ ```
174
+
175
+ #### Price data properties
176
+
177
+ | Name | Type | Description |
178
+ | ----------- | --------- | ------------ |
179
+ | id | `Integer` | league ID |
180
+ | name | `String` | league name |
181
+ | display | `String` | league display name |
182
+ | current | `Integer` | nb of items currently on sale |
183
+ | daily | `Integer` | nb of items found per 24h |
184
+ | accepted | `Integer` | nb of items accepted for price calculation |
185
+ | exalted | `Float` | mean price in exalted |
186
+ | max | `Float` | max accepted average price |
187
+ | min | `Float` | min accepted average price |
188
+ | mean | `Float` | mean average price |
189
+ | median | `Float` | median average price |
190
+ | mode | `Float` | mode average price |
191
+ | active | `Boolean` | is league still active |
192
+ | start | `String` | league end date |
193
+ | end | `String` | league end date |
194
+
195
+ ### Leagues
196
+
197
+ #### Get all league data
198
+
199
+ ```ruby
200
+ PoeWatch::League.all # => array of PoeWatch::League objects
201
+ PoeWatch::League.count # => total number of leagues
202
+ ```
203
+
204
+ #### Get multiple specific leagues
205
+ ```ruby
206
+ leagues = PoeWatch::League.where(hardcore: false) # Returns all non hardcore leagues
207
+ leagues = PoeWatch::League.where(name: /metamorph/i) # You can use regular expressions or strings for your queries
208
+
209
+ leagues.first.name # => "Hardcore Metamorph"
210
+ leagues.first.hardcore? # => true
211
+ leagues.first.start? # => "2019-12-13T20:00:00Z"
212
+ ```
213
+
214
+ #### Find one specific league
215
+ ```ruby
216
+ league = PoeWatch::League.find(name: "Metamorph") # Return the non hardcore "Metamorph" league
217
+ league = PoeWatch::League.find(name: /metamorph/i) # Will return the first Metamorph league found
218
+
219
+ league.name # => "Metamorph"
220
+ league.hardcore? # => false
221
+ league.start? # => "2019-12-13T20:00:00Z"
222
+ ```
223
+
224
+ #### League properties
225
+
226
+ All properties have an accessor you can use, e.g: `league.id`, `league.name`, `league.hardcore`.
227
+ All boolean properties have an additionnal question mark accessor, e.g: `league.hardcore?`.
228
+
229
+ | Name | Type | Description |
230
+ | -------- | --- | ---------------- |
231
+ |id | `Integer` | poe.watch ID |
232
+ |name | `String` | name |
233
+ |display | `String` | display name |
234
+ |hardcore | `Boolean` | is hardcore |
235
+ |active | `Boolean` | is active |
236
+ |start | `String` | start date |
237
+ |end | `String` | end date |
238
+ |challenge | `Boolean` | is a challenge league |
239
+ |event | `Boolean` | is an event league |
240
+ |upcoming | `Boolean` | is upcoming |
241
+
242
+ ### Categories
243
+
244
+ #### Get all categories data
245
+
246
+ ```ruby
247
+ PoeWatch::Categories.all # => array of PoeWatch::Category objects
248
+ PoeWatch::Categories.count # => total number of categories
249
+ ```
250
+
251
+ #### Get multiple specific categories
252
+ ```ruby
253
+ categories = PoeWatch::Category.where(name: /accessory/i) # You can use regular expressions or strings for your queries
254
+
255
+ categories.first.display # => "Accessories"
256
+ ```
257
+
258
+ #### Find one specific category
259
+ ```ruby
260
+ category = PoeWatch::Category.find(name: /accessory/i) # You can use regular expressions or strings for your queries
261
+
262
+ category.display # => "Accessories"
263
+ category.groups # => [#<OpenStruct id=1, name="amulet", display="Amulets">, ...]
264
+ category.groups.first.name # => "amulet"
265
+ ```
266
+
267
+ #### Category properties
268
+
269
+ All properties have an accessor you can use, e.g: `category.name`, `category.groups`.
270
+
271
+ | Name | Type | Description |
272
+ | -------- | --- | ---------------- |
273
+ |id | `Integer` | poe.watch ID |
274
+ |name | `String` | name |
275
+ |display | `String` | display name |
276
+ |groups | `Array<OpenStruct>` | an array of item types that belongs to this category. Stored as OpenStructs so you can access them with the `.` syntax. A group has 3 attributes: `id`, `name` and `display` |
277
+
278
+ ### API Cache
279
+
280
+ ```ruby
281
+ # Fetch all the data from poe.watch API and cache it for the next 45 minutes.
282
+ # You don't actually need to call this. It is automatically called when using `::find`, `::where`, `::all` or `::count`, and won't do anything if the cache already contains data.
283
+ # Although beware that if you don't call it first, the first call to find/where/all/count every 45 minutes will take longer (around 2-4 seconds, there are quite a lot of items to fetch).
284
+ PoeWatch::Api.refresh!
285
+ # You can also specify a cache TTL different than 45 minutes
286
+ PoeWatch::Api.refresh!(3600) # 1 hour cache
287
+
288
+ # If you need to force clear the cache, you can use
289
+ PoeWatch::Api.clear!
290
+
291
+ # You can check if the cache has expired with
292
+ PoeWatch::Api.has_expired?
293
+ # Or do the opposite with
294
+ PoeWatch::Api.ready?
295
+ ```
296
+
297
+ ## Shortcomings
298
+
299
+ - Doing a find or where on an item will always take some time (1-2 seconds) because it has to traverse all ~30k objects from PoE to find your matches. This could probably be optimised but I haven't had the time to get around to do it.
300
+ - No rate limiter set for the moment so when fetching a bunch of item prices you could hit the rate limiter of `poe.watch`. I'll add one at some point in the future if is needed or requested.
301
+
302
+ ## Contribution
303
+
304
+ The code is well documented, using [tomdoc](http://tomdoc.org/), is you wish to contribute, raise an issue or fork this project and create a pull request :).
305
+
306
+ Most of the codebase is in `lib/api`, `lib/base` and `lib/item`.
307
+
308
+ ## License
309
+
310
+ Licensed under WTFPL.
@@ -0,0 +1,175 @@
1
+ # TODO: Check item fields
2
+ # @base_is_elder=false,
3
+ # @base_is_shaper=true,
4
+ # @base_item_level=86,
5
+ # @enchant_max=25,
6
+ # @enchant_min=25,
7
+ # corrupted field
8
+
9
+ require 'redis'
10
+ require 'uri'
11
+ require 'net/http'
12
+
13
+ # Public: Wrapper around the Poe Watch API.
14
+ # Uses redis to store items, leagues and categories data
15
+ #
16
+ # Examples
17
+ #
18
+ # PoeWatch::Api.refresh! # => fetches the latest data from the API
19
+ #
20
+ # PoeWatch::Item.all # => All items
21
+ # PoeWatch::Item.find({ name: 'Mirror of Kalandra' }).getPriceDataByLeague('Metamorph')
22
+ #
23
+ # PoeWatch::League.find({ name: 'Metamorph' })
24
+ #
25
+ module PoeWatch
26
+ class Api
27
+ class Error < StandardError; end
28
+
29
+ BULK_APIS = {
30
+ item_data: "https://api.poe.watch/itemdata",
31
+ categories: "https://api.poe.watch/categories",
32
+ leagues: "https://api.poe.watch/leagues",
33
+ }
34
+
35
+ ITEM_API = "https://api.poe.watch/item"
36
+
37
+ DEFAULT_EXPIRY = 45 * 60 # 45 minutes
38
+
39
+ @updating = false
40
+ @redis = $redis
41
+
42
+ class << self
43
+ # Returns the redis instance, defaults to the global $redis
44
+ def redis
45
+ raise PoeWatch::Api::Error.new("You need to configure redis by either setting the global $redis variable or calling PoeWatchApi.redis = Redis.new(...)") unless (@redis || $redis)
46
+ @redis || $redis
47
+ end
48
+
49
+ # Sets the redis instance
50
+ def redis=(redis)
51
+ @redis = redis
52
+ end
53
+
54
+ # Public: Count the number of items
55
+ #
56
+ # expiry - The time to live (TTL) for the redis cache, defaults to 45 minutes.
57
+ #
58
+ # Examples
59
+ #
60
+ # PoeWatch::Api.refresh!
61
+ # PoeWatch::Api.refresh!(3600) # => 1 hour cache
62
+ #
63
+ # Returns an Integer.
64
+ def refresh!(expiry = DEFAULT_EXPIRY)
65
+ # If we are not already updating and the cache is empty
66
+ if !@updating && !ready?
67
+ #puts "Updating..."
68
+ @updating = true
69
+
70
+ # Fetch each API
71
+ BULK_APIS.each do |name, api_url|
72
+ raw_response = request(api_url)
73
+ redis.set(redis_key(name), raw_response)
74
+ redis.expire(redis_key(name), expiry)
75
+ end
76
+ @updating = false
77
+ true
78
+ else
79
+ return raise PoeWatch::Api::Error.new("An update is already in progress") if @updating
80
+ # puts "Already up to date"
81
+ end
82
+ end
83
+
84
+ # Internal: Does a get request
85
+ #
86
+ # api - api url
87
+ # params - a hash of params
88
+ # parse - if the data should be parsed as JSON or returned as String. Defaults to false.
89
+ #
90
+ # Examples
91
+ #
92
+ # PoeWatch::Api.request(BULK_APIS[:leagues])
93
+ #
94
+ # Returns a String or a Hash.
95
+ def request(api, params = {}, parse = false)
96
+ uri = URI.parse(api)
97
+
98
+ if (params.any?)
99
+ uri.query = URI.encode_www_form(params)
100
+ end
101
+
102
+ request = Net::HTTP::Get.new(uri.request_uri)
103
+ request["Content-Type"] = "application/json"
104
+
105
+ http = Net::HTTP.new(uri.hostname, uri.port)
106
+ http.use_ssl = true
107
+
108
+ response = http.request(request)
109
+
110
+ raise PoeWatch::Api::Error.new("Couldn't connect to poe.watch") unless response.is_a?(Net::HTTPSuccess)
111
+
112
+ parse ? JSON.parse(response.body) : response.body
113
+ end
114
+
115
+ # Returns all items
116
+ def items
117
+ @items ||= get_data(:item_data)
118
+ @items || []
119
+ end
120
+
121
+ # Returns all categories
122
+ def categories
123
+ @categories ||= get_data(:categories)
124
+ @categories || []
125
+ end
126
+
127
+ # Returns all leagues
128
+ def leagues
129
+ @leagues ||= get_data(:leagues)
130
+ @leagues || []
131
+ end
132
+
133
+ # Get data from redis and parse it to JSON
134
+ def get_data(type)
135
+ data = redis.get(redis_key(type))
136
+ data ? JSON.parse(data) : nil
137
+ end
138
+
139
+ # Clear the redis cache
140
+ def clear!
141
+ BULK_APIS.keys
142
+ .each { |key| redis.del(redis_key(key)) }
143
+ end
144
+
145
+ # Is the cache filled?
146
+ def ready?
147
+ !has_expired?
148
+ end
149
+
150
+ # Check if any of the redis stored data is missing
151
+ def has_expired?
152
+ BULK_APIS.keys
153
+ .map { |key| redis.exists(redis_key(key)) }
154
+ .reject { |k| !k }
155
+ .empty?
156
+ end
157
+
158
+ # Returns the redis cache key, e.g: poe_watch_itemdata, etc...
159
+ def redis_key(type)
160
+ "poe_watch_#{type}"
161
+ end
162
+
163
+ # Returns the redis memory footprint for each api data (leagues, itemdata and categories) in kilobytes.
164
+ def memory_footprint
165
+ BULK_APIS.keys.map do |key|
166
+ size = nil
167
+ if redis.exists(redis_key(key))
168
+ size = redis.debug("object", redis_key(key)).match(/serializedlength:(\d+)/)[1].to_i / 1024.0
169
+ end
170
+ [key, size]
171
+ end.to_h.compact
172
+ end
173
+ end
174
+ end
175
+ end
@@ -0,0 +1,172 @@
1
+ require_relative './utils'
2
+
3
+ # Public: Base class for all Poe Watch items.
4
+ # This is pretty much active record for PoeWatch.
5
+ # It automatically fetches the necessary data and defines getters, setters as well as question mark methods for boolean values.
6
+ # It also defines `::find(params)`, `::where(params)` and `::all` class methods.
7
+ #
8
+ # Examples
9
+ #
10
+ # class PoeWatch::Item < PoeWatch::Base
11
+ # end
12
+ #
13
+ module PoeWatch
14
+ class Base
15
+ INFLECTOR = {
16
+ category: 'categories',
17
+ item: 'items',
18
+ league: 'leagues'
19
+ }
20
+
21
+ class << self
22
+ # Defines an instance variable that'll be memoized and used to store Poe Watch data
23
+ def __data
24
+ if not defined? @__data
25
+ @__data = []
26
+ end
27
+ @__data
28
+ end
29
+
30
+ # Setter for instance variable
31
+ def __data=(value)
32
+ @__data = value
33
+ end
34
+
35
+ # Public: Gets the object type
36
+ #
37
+ # Examples
38
+ #
39
+ # PoeWatch::League.type # => :league
40
+ #
41
+ # Returns a Symbol.
42
+ def type
43
+ self.name.split('::').last.downcase.to_sym
44
+ end
45
+
46
+ # Public: Gets all data as instance of the class
47
+ #
48
+ # Examples
49
+ #
50
+ # PoeWatch::League.all # => array of PoeWatch::League items
51
+ #
52
+ # Returns an Array of class instances.
53
+ def all
54
+ PoeWatch::Api.refresh!
55
+
56
+ return __data if __data.any?
57
+
58
+ # If data isn't already loaded
59
+ # puts "Loading #{type} data..."
60
+ json_data = PoeWatch::Api.send(INFLECTOR[type])
61
+ if json_data.any?
62
+ __data = json_data.map { |raw_data| self.new(raw_data) }
63
+ else
64
+ []
65
+ end
66
+ end
67
+
68
+ # Public: Count the number of items
69
+ #
70
+ # Examples
71
+ #
72
+ # PoeWatch::League.count # => 30
73
+ #
74
+ # Returns an Integer.
75
+ def count
76
+ PoeWatch::Api.refresh!
77
+
78
+ return __data if __data.any?
79
+
80
+ # If data isn't already loaded
81
+ # puts "Loading #{type} data..."
82
+ json_data = PoeWatch::Api.send(INFLECTOR[type])
83
+ if json_data.any?
84
+ __data = json_data.length
85
+ else
86
+ 0
87
+ end
88
+ end
89
+
90
+ # Public: Gets items filtered by a query
91
+ # You can pass regular expressions for string parameters. It will do a match.
92
+ #
93
+ # params - A hash of parameters. Eg: { hardcore: true }
94
+ #
95
+ # Examples
96
+ #
97
+ # PoeWatch::League.where({ hardcore: true }) # => array of PoeWatch::League items
98
+ # PoeWatch::League.where({ name: /metamorph/i, hardcore: true }) # => array of PoeWatch::League items
99
+ #
100
+ # Returns an Array of class instances.
101
+ def where(params)
102
+ PoeWatch::Api.refresh!
103
+ self.all.select do |element|
104
+ params.map do |k, v|
105
+ if v.is_a? Regexp
106
+ !!element.send(k).match(v)
107
+ else
108
+ element.send(k) == v
109
+ end
110
+ end.all?(TrueClass)
111
+ end
112
+ end
113
+
114
+ # Public: Gets a specific item
115
+ # You can pass regular expressions for string parameters. It will do a match.
116
+ #
117
+ # params - A hash of parameters. Eg: { name: /Metamorph/, hardcore: true }
118
+ #
119
+ # Examples
120
+ #
121
+ # PoeWatch::League.find({ name: "Metamorph", hardcore: true }) # =>PoeWatch::League item
122
+ #
123
+ # Returns an instance of the class.
124
+ def find(params)
125
+ PoeWatch::Api.refresh!
126
+ self.all.find do |element|
127
+ params.map do |k, v|
128
+ if v.is_a? Regexp
129
+ !!element.send(k).match(v)
130
+ else
131
+ element.send(k) == v
132
+ end
133
+ end.all?(TrueClass)
134
+ end
135
+ end
136
+ end
137
+
138
+ # Public: Initialize a Poe Watch item.
139
+ # It will automatically create instance variables, setters, getters for instance variables,
140
+ # and question mark methods for variables with boolean values.
141
+ # It is to note that any hash or array of hashes will be transformed into an OpenStruct.
142
+ #
143
+ # raw_data - An array of hash
144
+ #
145
+ # Returns an instance of the class.
146
+ def initialize(raw_data)
147
+ raw_data.each do |k, v|
148
+ key = snakify(k)
149
+ if v.is_a?(Hash)
150
+ value = OpenStruct.new(v)
151
+ elsif v.is_a?(Array) && v.first.is_a?(Hash)
152
+ value = v.map { |arr_val| OpenStruct.new(arr_val) }
153
+ else
154
+ value = v
155
+ end
156
+
157
+ instance_variable_set("@#{key}", value)
158
+
159
+ define_singleton_method key do
160
+ instance_variable_get("@#{key}")
161
+ end
162
+
163
+ # If it is a boolean we also create a `key?` method`
164
+ if [TrueClass, FalseClass].include?(value.class)
165
+ define_singleton_method "#{key}?" do
166
+ instance_variable_get("@#{key}")
167
+ end
168
+ end
169
+ end
170
+ end
171
+ end
172
+ end
@@ -0,0 +1,6 @@
1
+ require_relative './base'
2
+
3
+ module PoeWatch
4
+ class Category < PoeWatch::Base
5
+ end
6
+ end
@@ -0,0 +1,19 @@
1
+ require_relative './base'
2
+ require_relative './price_data'
3
+
4
+ module PoeWatch
5
+ class Item < PoeWatch::Base
6
+ def prices
7
+ @prices ||= PoeWatch::Api.request(PoeWatch::Api::ITEM_API, { id: self.id }, true)["leagues"].map { |league_data| PoeWatch::PriceData.new(league_data) }
8
+ end
9
+
10
+ def price_for_leagues(name_or_regexp)
11
+ regexp = name_or_regexp.is_a?(String) ? Regexp.new(name_or_regexp, 'i') : name_or_regexp
12
+ prices.select { |data| data.name.match(regexp) }
13
+ end
14
+
15
+ def price_for_league(name_or_regexp)
16
+ prices.find { |data| name_or_regexp.is_a?(String) ? data.name.downcase == name_or_regexp.downcase : data.name.match(name_or_regexp) }
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,6 @@
1
+ require_relative './base'
2
+
3
+ module PoeWatch
4
+ class League < PoeWatch::Base
5
+ end
6
+ end
@@ -0,0 +1,5 @@
1
+ require_relative './api'
2
+ require_relative './item'
3
+ require_relative './league'
4
+ require_relative './category'
5
+
@@ -0,0 +1,4 @@
1
+ require_relative './base'
2
+
3
+ class PoeWatch::PriceData < PoeWatch::Base
4
+ end
@@ -0,0 +1,7 @@
1
+ def snakify(string)
2
+ string.gsub(/::/, '/').
3
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
4
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
5
+ tr("-", "_").
6
+ downcase
7
+ end
@@ -0,0 +1,3 @@
1
+ module PoeWatch
2
+ VERSION = "1.0.0"
3
+ end
metadata ADDED
@@ -0,0 +1,67 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: poe-watch-api
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - GabrielDehan
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2019-01-13 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: redis
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 4.1.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 4.1.0
27
+ description: Ruby Poe Watch API wrapper.
28
+ email: dehan.gabriel@gmail.com
29
+ executables: []
30
+ extensions: []
31
+ extra_rdoc_files: []
32
+ files:
33
+ - LICENSE
34
+ - README.md
35
+ - lib/api.rb
36
+ - lib/base.rb
37
+ - lib/category.rb
38
+ - lib/item.rb
39
+ - lib/league.rb
40
+ - lib/poe_watch.rb
41
+ - lib/price_data.rb
42
+ - lib/utils.rb
43
+ - lib/version.rb
44
+ homepage: https://github.com/gabriel-dehan/poe-watch-api
45
+ licenses: []
46
+ metadata: {}
47
+ post_install_message:
48
+ rdoc_options: []
49
+ require_paths:
50
+ - lib
51
+ required_ruby_version: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ required_rubygems_version: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ requirements: []
62
+ rubyforge_project:
63
+ rubygems_version: 2.7.6
64
+ signing_key:
65
+ specification_version: 4
66
+ summary: Ruby Poe Watch API wrapper.
67
+ test_files: []