piotr_majewski_magic 0.0.1 → 0.0.2
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.
- checksums.yaml +4 -4
- data/bin/query_mtg +2 -2
- data/lib/cards_manager.rb +52 -0
- data/lib/cards_source.rb +39 -0
- data/lib/mtg_api_request.rb +61 -0
- data/lib/mtg_api_response.rb +14 -0
- data/lib/query.rb +45 -0
- data/lib/query_mtg.rb +25 -20
- metadata +9 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a14d3c066a0bf4c520e2ec0850ecd34ba99767f2a17658114d808e0a63241e61
|
4
|
+
data.tar.gz: d3e2ca74fd6f3f9a0d37f51e201157afcee5d40ddf1118957811c3041aefee69
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1bb49e447b525acdc35f32ea9880103808d6bb6d1fcf3fcec1610601253a579b0c480a8835ef403528fa2f95803812d332abf31a7c52273df64177673042bf08
|
7
|
+
data.tar.gz: 03dc6ced9fa57fac243d2c9e38f8eb39312e10fc04a537945516af8d7b45d33e4d0300c1c7c26c3ebfcec6f3a008f5ca56a2a88d5a27e95028a12996f558a32d
|
data/bin/query_mtg
CHANGED
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'query'
|
4
|
+
|
5
|
+
class CardsManager
|
6
|
+
InvalidValue = Class.new(StandardError)
|
7
|
+
UnknownAttribute = Class.new(StandardError)
|
8
|
+
|
9
|
+
attr_reader :query, :cards_source
|
10
|
+
|
11
|
+
def initialize(query, cards_source)
|
12
|
+
@query = query
|
13
|
+
@cards_source = cards_source
|
14
|
+
end
|
15
|
+
|
16
|
+
def requested_cards
|
17
|
+
return cards_source if query.empty?
|
18
|
+
|
19
|
+
group_and_select(query, cards_source)
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def group_and_select(query, cards)
|
25
|
+
raise UnknownAttribute unless cards.first.key?(query[0][:attribute])
|
26
|
+
|
27
|
+
selected_cards = matching_cards(query[0], cards)
|
28
|
+
new_query = query[1..]
|
29
|
+
|
30
|
+
return selected_cards if new_query.empty?
|
31
|
+
|
32
|
+
selected_cards.map.with_object({}) do |(attribute_value, cards), hash|
|
33
|
+
hash[attribute_value] = group_and_select(new_query, cards)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def matching_cards(filter, cards)
|
38
|
+
cards.group_by { |card| card[filter[:attribute]] }
|
39
|
+
.select { |key, _| match?(filter[:value], key) }
|
40
|
+
end
|
41
|
+
|
42
|
+
def match?(cli_value, source_value)
|
43
|
+
return true if cli_value == Query::Group
|
44
|
+
return cli_value == source_value if [cli_value, source_value].all? { |val| val.is_a?(String) }
|
45
|
+
return cli_value.to_i == source_value if source_value.is_a?(Numeric)
|
46
|
+
|
47
|
+
cli_value.tally == source_value&.tally
|
48
|
+
rescue NoMethodError
|
49
|
+
message = "`#{cli_value}`: Type of provided value doesn't match type of card attribute"
|
50
|
+
raise InvalidValue, message
|
51
|
+
end
|
52
|
+
end
|
data/lib/cards_source.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
require_relative 'mtg_api_response'
|
5
|
+
|
6
|
+
class CardsSource
|
7
|
+
def self.get(no_cache = nil)
|
8
|
+
new(no_cache).get
|
9
|
+
end
|
10
|
+
|
11
|
+
def get
|
12
|
+
JSON.parse(cards_source)
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
attr_reader :no_cache
|
18
|
+
|
19
|
+
def initialize(no_cache)
|
20
|
+
@no_cache = no_cache
|
21
|
+
end
|
22
|
+
|
23
|
+
def cards_source
|
24
|
+
return File.read(cache_file_path) if File.file?(cache_file_path) && !no_cache
|
25
|
+
|
26
|
+
cache_cards
|
27
|
+
File.read(cache_file_path)
|
28
|
+
end
|
29
|
+
|
30
|
+
def cache_cards
|
31
|
+
File.open(cache_file_path, 'w') do |file|
|
32
|
+
file.write(MtgApiResponse.results)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def cache_file_path
|
37
|
+
File.expand_path('../cache/cards.json', __dir__)
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'typhoeus'
|
4
|
+
require 'typhoeus/adapters/faraday'
|
5
|
+
require 'json'
|
6
|
+
|
7
|
+
class MtgApiRequest
|
8
|
+
URL = 'https://api.magicthegathering.io/v1/cards'
|
9
|
+
PARALLEL_COUNT = 40
|
10
|
+
|
11
|
+
attr_reader :results
|
12
|
+
|
13
|
+
def initialize
|
14
|
+
@results = []
|
15
|
+
end
|
16
|
+
|
17
|
+
def make
|
18
|
+
while unfetched_pages.any?
|
19
|
+
@temp_results = []
|
20
|
+
connection.in_parallel do
|
21
|
+
unfetched_pages.sample(PARALLEL_COUNT).each do |page|
|
22
|
+
fetch_page(page)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
process_results(@temp_results)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def unfetched_pages
|
33
|
+
@unfetched_pages ||= (1..total_count).to_a
|
34
|
+
end
|
35
|
+
|
36
|
+
def total_count
|
37
|
+
response = Faraday.get(URL)
|
38
|
+
(response.headers['total-count'].to_f / 100).ceil
|
39
|
+
end
|
40
|
+
|
41
|
+
def connection
|
42
|
+
@connection ||= Faraday.new(url: URL) { |faraday| faraday.adapter :typhoeus }
|
43
|
+
end
|
44
|
+
|
45
|
+
def fetch_page(page)
|
46
|
+
url = URL + "?page=#{page}"
|
47
|
+
puts "Fetching: #{url}"
|
48
|
+
response = connection.get(url)
|
49
|
+
|
50
|
+
@temp_results << [page, response]
|
51
|
+
end
|
52
|
+
|
53
|
+
def process_results(temp_results)
|
54
|
+
temp_results.each do |page, result|
|
55
|
+
next unless result.status == 200
|
56
|
+
|
57
|
+
unfetched_pages.delete(page)
|
58
|
+
results << JSON.parse(result.body)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
data/lib/query.rb
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'forwardable'
|
4
|
+
|
5
|
+
class Query
|
6
|
+
extend Forwardable
|
7
|
+
|
8
|
+
RepeatedFilter = Class.new(StandardError)
|
9
|
+
Group = Class.new
|
10
|
+
|
11
|
+
attr_reader :argv
|
12
|
+
|
13
|
+
def initialize(argv)
|
14
|
+
@argv = argv
|
15
|
+
end
|
16
|
+
|
17
|
+
def build
|
18
|
+
@build ||= argv.map.with_object([]) do |argument, query_options|
|
19
|
+
query_element = parse_argument(argument)
|
20
|
+
|
21
|
+
if query_options.find { |el| el[:attribute] == query_element[:attribute] }
|
22
|
+
message = "#{query_element[:attribute]}: Every filter can be passed only once"
|
23
|
+
raise RepeatedFilter, message
|
24
|
+
end
|
25
|
+
|
26
|
+
query_options << query_element
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def_delegators :build, :empty?
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
# TODO: pick a divider (something that is unlikely to show up)
|
35
|
+
# potential problem with imageUrl values
|
36
|
+
# NOTE: should allow escaping `#`
|
37
|
+
def parse_argument(argument)
|
38
|
+
return { attribute: argument, value: Group } unless argument.match?('=')
|
39
|
+
|
40
|
+
attribute, value = argument.split('=')
|
41
|
+
value = value&.match?('#') ? value.split('#') : value
|
42
|
+
|
43
|
+
{ attribute: attribute, value: value }
|
44
|
+
end
|
45
|
+
end
|
data/lib/query_mtg.rb
CHANGED
@@ -1,34 +1,39 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require '
|
3
|
+
require 'json'
|
4
|
+
require_relative 'cards_manager'
|
5
|
+
require_relative 'query'
|
6
|
+
require_relative 'cards_source'
|
7
|
+
|
4
8
|
class QueryMtg
|
5
|
-
def self.
|
6
|
-
new(argv).
|
9
|
+
def self.call(argv)
|
10
|
+
new(argv).call
|
7
11
|
end
|
8
12
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
if query_options[attribute.to_sym]
|
15
|
-
query_options[attribute.to_sym][:values] << value
|
16
|
-
else
|
17
|
-
query_options[attribute.to_sym] = {
|
18
|
-
priority: @grouping_priority, values: [value]
|
19
|
-
}
|
20
|
-
@grouping_priority += 1
|
21
|
-
end
|
22
|
-
end
|
13
|
+
def call
|
14
|
+
puts requested_cards
|
15
|
+
rescue Query::RepeatedFilter, CardsManager::InvalidValue, CardsManager::UnknownAttribute => e
|
16
|
+
puts e.message
|
23
17
|
end
|
24
|
-
# rubocop:enable Metrics/MethodLength
|
25
18
|
|
26
19
|
private
|
27
20
|
|
28
|
-
attr_reader :argv
|
21
|
+
attr_reader :no_cache, :argv
|
29
22
|
|
30
23
|
def initialize(argv)
|
24
|
+
@no_cache = argv.delete('--no-cache')
|
31
25
|
@argv = argv
|
32
|
-
|
26
|
+
end
|
27
|
+
|
28
|
+
def requested_cards
|
29
|
+
CardsManager.new(query, cards_source).requested_cards
|
30
|
+
end
|
31
|
+
|
32
|
+
def query
|
33
|
+
Query.new(argv).build
|
34
|
+
end
|
35
|
+
|
36
|
+
def cards_source
|
37
|
+
CardsSource.get(no_cache)
|
33
38
|
end
|
34
39
|
end
|
metadata
CHANGED
@@ -1,17 +1,17 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: piotr_majewski_magic
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Piotr Majewski
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-07-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: faraday
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
@@ -25,7 +25,7 @@ dependencies:
|
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '1.4'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
28
|
+
name: typhoeus
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
@@ -47,6 +47,11 @@ extensions: []
|
|
47
47
|
extra_rdoc_files: []
|
48
48
|
files:
|
49
49
|
- bin/query_mtg
|
50
|
+
- lib/cards_manager.rb
|
51
|
+
- lib/cards_source.rb
|
52
|
+
- lib/mtg_api_request.rb
|
53
|
+
- lib/mtg_api_response.rb
|
54
|
+
- lib/query.rb
|
50
55
|
- lib/query_mtg.rb
|
51
56
|
homepage: http://rubygems.org/gems/piotr_majewski_magic
|
52
57
|
licenses:
|