piotr_majewski_magic 0.0.1 → 0.0.6

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA256:
3
- metadata.gz: 642fea55869de823824c5e99ddfc05c507accdec58e748a747e91ff2fe0f296d
4
- data.tar.gz: 319f695786b0ab39013930735a6ef5a9324bd8d9a6b574b4f6d0fb44d72a3428
2
+ SHA1:
3
+ metadata.gz: 501de2d0b9fd09066c8e4adc35a4fd690ff74e8e
4
+ data.tar.gz: 32250955f24b4476b04aad15e63c690ccad9cf2c
5
5
  SHA512:
6
- metadata.gz: 5b3c439a7b259903abae0d0f44f8db94f198dbad02baf005ec583a92293c9486b3a0294d8f94337c3df1dcc2aad278cb10b4aa57d1aa3886fc717a61eb273be8
7
- data.tar.gz: 74a0851d96fbb1dc45e838c9522c0a6ac05d1a331d07f7e4274ac1cd283c9132c9c4f23572d79b46d5431627f61b44f500a60b080c20798b29f252d3006fc1ee
6
+ metadata.gz: b6e77141adff2298a2788db314a88f38a9c90319777ef90ab270ad3d5e904970103c4dc89098aafec31147350998b0fdb3d9b5efe40b1819a45d6d2236b4afe5
7
+ data.tar.gz: a0ee9174bff5d8ae56709ef7ebd4145b9789fe84be2a91141466cb792c16d6362d83472f889df420cc7af75f6791614a853ea4d897555222571ac5a5c8e5e326
data/bin/query_mtg CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
- require_relative '../lib/query_mtg.rb'
4
+ require_relative '../lib/query_mtg'
4
5
 
5
6
  QueryMtg.call(ARGV)
6
-
data/cache/cards.json ADDED
File without changes
@@ -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
@@ -0,0 +1,46 @@
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
+ fetch_cards_from_api if no_cache || cache_empty?
25
+
26
+ read_cache
27
+ end
28
+
29
+ def read_cache
30
+ File.read(cache_file_path)
31
+ end
32
+
33
+ def cache_empty?
34
+ File.size(cache_file_path).zero?
35
+ end
36
+
37
+ def fetch_cards_from_api
38
+ File.open(cache_file_path, 'w') do |file|
39
+ file.write(MtgApiResponse.results)
40
+ end
41
+ end
42
+
43
+ def cache_file_path
44
+ File.expand_path('../cache/cards.json', __dir__)
45
+ end
46
+ 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
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'mtg_api_request'
4
+
5
+ class MtgApiResponse
6
+ def self.results
7
+ request = MtgApiRequest.new
8
+ request.make
9
+
10
+ request.results
11
+ .flat_map { |page| page['cards'] }
12
+ .to_json
13
+ end
14
+ 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,40 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'pry'
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.build_query(argv)
6
- new(argv).build_query
9
+ def self.call(argv)
10
+ new(argv).call
7
11
  end
8
12
 
9
- # rubocop:disable Metrics/MethodLength
10
- def build_query
11
- argv.map.with_object({}) do |argument, query_options|
12
- attribute, value = argument.split('=')
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
+ # NOTE: puts to output to CLI
14
+ def call
15
+ puts requested_cards
16
+ rescue Query::RepeatedFilter, CardsManager::InvalidValue, CardsManager::UnknownAttribute => e
17
+ puts e.message
23
18
  end
24
- # rubocop:enable Metrics/MethodLength
25
19
 
26
20
  private
27
21
 
28
- attr_reader :argv
22
+ attr_reader :no_cache, :argv
29
23
 
30
24
  def initialize(argv)
25
+ @no_cache = argv.delete('--no-cache')
31
26
  @argv = argv
32
- @grouping_priority = 0
27
+ end
28
+
29
+ def requested_cards
30
+ CardsManager.new(query, cards_source).requested_cards
31
+ end
32
+
33
+ def query
34
+ Query.new(argv).build
35
+ end
36
+
37
+ def cards_source
38
+ CardsSource.get(no_cache)
33
39
  end
34
40
  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.1
4
+ version: 0.0.6
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-06-30 00:00:00.000000000 Z
11
+ date: 2021-07-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: typhoeus
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: faraday
28
+ name: typhoeus
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
@@ -47,6 +47,12 @@ extensions: []
47
47
  extra_rdoc_files: []
48
48
  files:
49
49
  - bin/query_mtg
50
+ - cache/cards.json
51
+ - lib/cards_manager.rb
52
+ - lib/cards_source.rb
53
+ - lib/mtg_api_request.rb
54
+ - lib/mtg_api_response.rb
55
+ - lib/query.rb
50
56
  - lib/query_mtg.rb
51
57
  homepage: http://rubygems.org/gems/piotr_majewski_magic
52
58
  licenses:
@@ -67,7 +73,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
67
73
  - !ruby/object:Gem::Version
68
74
  version: '0'
69
75
  requirements: []
70
- rubygems_version: 3.1.4
76
+ rubyforge_project:
77
+ rubygems_version: 2.6.12
71
78
  signing_key:
72
79
  specification_version: 4
73
80
  summary: CLI for querying MTG.io