magic_tcg_finder 0.1.0 → 0.2.0
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/config/environment.rb +8 -0
- data/lib/magic_tcg_finder/card.rb +31 -0
- data/lib/magic_tcg_finder/command_line_interface.rb +187 -0
- data/lib/magic_tcg_finder/scraper.rb +95 -0
- metadata +7 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fc50cb1eec2425c089d79ddb627e515ab7a3e65d
|
4
|
+
data.tar.gz: 3fa482305de2d27ade8db72ec5adc2b09d627016
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 22aebe3f7a81c4afd59e7d3e67cbc0b19263b56b5b135c6cad85f10fd5da2c4623231215fb4c9d83c8061c08db35b54c7fe31cf73fff9acc061dbea1c531baf2
|
7
|
+
data.tar.gz: 04547d510c8835ade0cfe1de2fe0a3287edc8fd0e83bd91a62f31d324daee8942fc6bf2a275d370f2f1561909072d26d7e34abd8d9bd13bbd4573708df720700
|
@@ -0,0 +1,31 @@
|
|
1
|
+
class Card
|
2
|
+
|
3
|
+
attr_accessor :name, :rarity, :sets, :price, :rules_text, :flavor_text, :color, :cost, :purchase_url, :card_type, :combat_stats, :url
|
4
|
+
|
5
|
+
@@all = []
|
6
|
+
|
7
|
+
def initialize(attributes)
|
8
|
+
attributes.each {|attribute, value| self.send(("#{attribute}="), value)}
|
9
|
+
@@all << self
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.all
|
13
|
+
@@all
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.destroy_all
|
17
|
+
self.all.clear
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.create_from_collection(card_arr)
|
21
|
+
card_arr.each do |card|
|
22
|
+
new_card = Card.new(card)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def add_card_attributes(attribute_hash)
|
27
|
+
attribute_hash.each {|attribute, value| self.send(("#{attribute}="), value)}
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
@@ -0,0 +1,187 @@
|
|
1
|
+
class CommandLine
|
2
|
+
|
3
|
+
|
4
|
+
def clear_screen
|
5
|
+
print %x{clear}
|
6
|
+
end
|
7
|
+
|
8
|
+
def start_screen
|
9
|
+
clear_screen
|
10
|
+
puts " _ _____ _ ___ _ _ _ ".colorize(:light_green)
|
11
|
+
puts " /\\/\\ __ _ __ _(_) ___ _ /__ \\ |__ ___ / _ \\__ _| |_| |__ ___ _ __(_)_ __ __ _ ".colorize(:light_green)
|
12
|
+
puts " / \\ / _` |/ _` | |/ __| (_) / /\\/ '_ \\ / _ \\ / /_\\/ _` | __| '_ \\ / _ \\ '__| | '_ \\ / _` |".colorize(:light_green)
|
13
|
+
puts "/ /\\/\\ \\ (_| | (_| | | (__ _ / / | | | | __/ / /_\\\\ (_| | |_| | | | __/ | | | | | | (_| |".colorize(:light_green)
|
14
|
+
puts "\\/ \\/\\__,_|\\__, |_|\\___| (_) \\/ |_| |_|\\___| \\____/\\__,_|\\__|_| |_|\\___|_| |_|_| |_|\\__, |".colorize(:light_green)
|
15
|
+
puts " |___/ |___/ ".colorize(:light_green)
|
16
|
+
puts " ___ _ ___ _ _ ".colorize(:light_green)
|
17
|
+
puts " / __\\__ _ _ __ __| | / __(_)_ __ __| | ___ _ __ ".colorize(:light_green)
|
18
|
+
puts " / / / _` | '__/ _` | / _\\ | | '_ \\ / _` |/ _ \\ '__| ".colorize(:light_green)
|
19
|
+
puts " / /__| (_| | | | (_| | / / | | | | | (_| | __/ | ".colorize(:light_green)
|
20
|
+
puts " \\____/\\__,_|_| \\__,_| \\/ |_|_| |_|\\__,_|\\___|_| ".colorize(:light_green)
|
21
|
+
|
22
|
+
puts ""
|
23
|
+
|
24
|
+
run
|
25
|
+
end
|
26
|
+
|
27
|
+
def get_query
|
28
|
+
puts "Please enter the name of a card you want to find"
|
29
|
+
gets.strip
|
30
|
+
end
|
31
|
+
|
32
|
+
def call_scraper(input)
|
33
|
+
new_search = Scraper.new(input)
|
34
|
+
new_search.scrape_search_page
|
35
|
+
end
|
36
|
+
|
37
|
+
def load_attributes(card)
|
38
|
+
card_hash = Scraper.scrape_card_page(card.url)
|
39
|
+
card.add_card_attributes(card_hash)
|
40
|
+
end
|
41
|
+
|
42
|
+
def create_cards(card_arr)
|
43
|
+
Card.create_from_collection(card_arr)
|
44
|
+
end
|
45
|
+
|
46
|
+
def list_cards
|
47
|
+
clear_screen
|
48
|
+
Card.all.each_with_index do |card, index|
|
49
|
+
puts "#{index + 1}.#{card.name}"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def access_list
|
54
|
+
max = Card.all.size
|
55
|
+
puts "Please make a selection"
|
56
|
+
input = gets.strip.to_i
|
57
|
+
if input < 0 || input > max
|
58
|
+
puts "Invalid selection"
|
59
|
+
card_menu
|
60
|
+
end
|
61
|
+
input
|
62
|
+
end
|
63
|
+
|
64
|
+
def colorize_cost(cost)
|
65
|
+
return cost if cost.nil?
|
66
|
+
cost = cost.gsub(/\{W\}/) { |match| match.colorize(:light_yellow) }
|
67
|
+
cost = cost.gsub(/\{U\}/) { |match| match.colorize(:light_cyan) }
|
68
|
+
cost = cost.gsub(/\{B\}/) { |match| match.colorize(:magenta) }
|
69
|
+
cost = cost.gsub(/\{R\}/) { |match| match.colorize(:red) }
|
70
|
+
cost = cost.gsub(/\{G\}/) { |match| match.colorize(:green) }
|
71
|
+
cost = cost.gsub(/\{T\}/) { |match| match.colorize(:light_black) }
|
72
|
+
cost = cost.gsub(/\{\d\}/) { |match| match.colorize(:light_black) }
|
73
|
+
end
|
74
|
+
|
75
|
+
def colorize_identity(card)
|
76
|
+
color = card.color
|
77
|
+
|
78
|
+
case color
|
79
|
+
when "White"
|
80
|
+
card.color.colorize(:light_yellow)
|
81
|
+
when "Blue"
|
82
|
+
card.color.colorize(:light_cyan)
|
83
|
+
when "Black"
|
84
|
+
card.color.colorize(:magenta)
|
85
|
+
when "Red"
|
86
|
+
card.color.colorize(:red)
|
87
|
+
when "Green"
|
88
|
+
card.color.colorize(:green)
|
89
|
+
when "Multicolor"
|
90
|
+
card.color.colorize(:yellow)
|
91
|
+
else
|
92
|
+
card.color.colorize(:light_black)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def load_card(input)
|
97
|
+
chosen_card = Card.all[input - 1]
|
98
|
+
load_attributes(chosen_card) if chosen_card.rules_text.nil?
|
99
|
+
puts "#{chosen_card.name} - #{colorize_cost(chosen_card.cost)}"
|
100
|
+
puts ""
|
101
|
+
puts "Color Identity: #{colorize_identity(chosen_card)}"
|
102
|
+
puts "Type: #{chosen_card.card_type}"
|
103
|
+
puts "Rarity: #{chosen_card.rarity}"
|
104
|
+
puts ""
|
105
|
+
puts "#{colorize_cost(chosen_card.rules_text)}"
|
106
|
+
puts "#{chosen_card.flavor_text}"
|
107
|
+
puts "#{chosen_card.combat_stats}" unless chosen_card.combat_stats.nil?
|
108
|
+
puts ""
|
109
|
+
puts "Appears in the following set(s): #{chosen_card.sets.join(', ')}"
|
110
|
+
puts ""
|
111
|
+
puts "Average price: $#{chosen_card.price}" unless chosen_card.price.nil?
|
112
|
+
puts "Purchase at #{chosen_card.purchase_url}"
|
113
|
+
puts ""
|
114
|
+
end
|
115
|
+
|
116
|
+
def card_menu
|
117
|
+
if Card.all.size == 1
|
118
|
+
clear_screen
|
119
|
+
load_card(1)
|
120
|
+
interaction
|
121
|
+
else
|
122
|
+
list_cards
|
123
|
+
input = access_list
|
124
|
+
clear_screen
|
125
|
+
load_card(input)
|
126
|
+
interaction
|
127
|
+
end
|
128
|
+
|
129
|
+
end
|
130
|
+
|
131
|
+
def interaction
|
132
|
+
puts "Please make a selection"
|
133
|
+
puts "1. Return to card list"
|
134
|
+
puts "2. New search"
|
135
|
+
puts "3. Exit"
|
136
|
+
input = gets.strip.downcase
|
137
|
+
case input
|
138
|
+
when "1"
|
139
|
+
card_menu
|
140
|
+
when "2"
|
141
|
+
clear_screen
|
142
|
+
run
|
143
|
+
when "3"
|
144
|
+
clear_screen
|
145
|
+
exit
|
146
|
+
else
|
147
|
+
puts "Invalid selection"
|
148
|
+
interaction
|
149
|
+
end
|
150
|
+
|
151
|
+
end
|
152
|
+
|
153
|
+
def try_again
|
154
|
+
puts ""
|
155
|
+
puts "Please make a selection:"
|
156
|
+
puts "1. Search for a new card"
|
157
|
+
puts "2. Exit this program"
|
158
|
+
input = gets.strip
|
159
|
+
|
160
|
+
case input
|
161
|
+
when "1"
|
162
|
+
clear_screen
|
163
|
+
run
|
164
|
+
when "2"
|
165
|
+
clear_screen
|
166
|
+
exit
|
167
|
+
else
|
168
|
+
"Please enter a valid selection"
|
169
|
+
try_again
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
def run
|
174
|
+
Card.destroy_all
|
175
|
+
query = get_query
|
176
|
+
clear_screen
|
177
|
+
card_arr = call_scraper(query)
|
178
|
+
if card_arr == []
|
179
|
+
try_again
|
180
|
+
end
|
181
|
+
create_cards(card_arr)
|
182
|
+
card_menu
|
183
|
+
interaction
|
184
|
+
end
|
185
|
+
|
186
|
+
|
187
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
class Scraper
|
2
|
+
|
3
|
+
SEARCH_URL = "https://scryfall.com/search?q="
|
4
|
+
SITE_URL = "https://scryfall.com"
|
5
|
+
|
6
|
+
attr_accessor :query
|
7
|
+
|
8
|
+
def initialize(query)
|
9
|
+
@query = query
|
10
|
+
end
|
11
|
+
|
12
|
+
def scrape_search_page(url = nil)
|
13
|
+
url = self.input_parser if url.nil?
|
14
|
+
begin
|
15
|
+
card_index = Nokogiri::HTML(open(url))
|
16
|
+
rescue
|
17
|
+
puts "No cards found"
|
18
|
+
return []
|
19
|
+
end
|
20
|
+
|
21
|
+
cards = card_index.css('a.card-grid-item')
|
22
|
+
if cards.empty?
|
23
|
+
[{name: 'placeholder', url: url}]
|
24
|
+
else
|
25
|
+
card_collection = []
|
26
|
+
cards.each do |card|
|
27
|
+
card_hash = {}
|
28
|
+
card_hash[:name] = card.css('img.card').attr('alt').text.gsub(/\s\(...\)/, "")
|
29
|
+
card_hash[:url] = SITE_URL + card['href']
|
30
|
+
card_collection << card_hash
|
31
|
+
end
|
32
|
+
if card_index.css("a.button-primary.js-paginate-forward").any?
|
33
|
+
#looks for more pages
|
34
|
+
new_url = SITE_URL + card_index.css("a.button-primary.js-paginate-forward").attr('href').text
|
35
|
+
card_collection << scrape_search_page(new_url)
|
36
|
+
#Recurses if it finds more pages
|
37
|
+
card_collection.flatten
|
38
|
+
else
|
39
|
+
card_collection
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
def input_parser
|
46
|
+
input = self.query.downcase.split.collect{|string|string.scan(/[a-z]/)}
|
47
|
+
input = input.collect {|arr|arr.join("")}.join('+')
|
48
|
+
SEARCH_URL + input
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.color_finder(cost)
|
52
|
+
colors = {'{W}' =>'White','{U}' => 'Blue','{B}' => 'Black','{R}' => 'Red','{G}' => 'Green'}
|
53
|
+
matches = 0
|
54
|
+
color_identity = "Colorless"
|
55
|
+
#Not all cards have a casting cost
|
56
|
+
return color_identity if cost.nil?
|
57
|
+
|
58
|
+
colors.each do |symbol, value|
|
59
|
+
if cost.include?(symbol)
|
60
|
+
matches += 1
|
61
|
+
color_identity = value
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
|
66
|
+
if matches > 1
|
67
|
+
"Multicolor"
|
68
|
+
else
|
69
|
+
color_identity
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
|
74
|
+
def self.scrape_card_page(profile_url)
|
75
|
+
card_hash = {}
|
76
|
+
card_profile = Nokogiri::HTML(open(profile_url))
|
77
|
+
title_text = card_profile.css('h1.card-text-title').text.strip.gsub(/\n/, '||').split("||")
|
78
|
+
card_hash[:name] = title_text.first
|
79
|
+
#Not all cards have a casting cost, such as a land... .gsub on nil makes ruby sad.
|
80
|
+
card_hash[:cost] = title_text[1].gsub(/\s/,"") unless title_text[1] == nil
|
81
|
+
card_hash[:rarity] = card_profile.css('span.prints-current-set-details').text.strip.split(', ')[1]
|
82
|
+
card_hash[:sets] = card_profile.css('tbody tr').collect {|element|element.css('td').text.strip[/^.*(?=(\n))/]}.delete_if{|element|element.nil?}
|
83
|
+
card_hash[:price] = card_profile.css('span.price.currency-usd').text.split('$')[1]
|
84
|
+
card_hash[:color] = self.color_finder(card_hash[:cost])
|
85
|
+
card_hash[:purchase_url] = card_profile.css('#stores > ul > li:nth-child(1) > a').attr('href').text
|
86
|
+
card_hash[:card_type] = card_profile.css('p.card-text-type-line').text.strip
|
87
|
+
card_hash[:combat_stats] = card_profile.css('div.card-text-stats').text.strip
|
88
|
+
card_hash[:rules_text] = card_profile.css('div.card-text-oracle').text.strip
|
89
|
+
card_hash[:flavor_text] = card_profile.css('div.card-text-flavor').text.strip
|
90
|
+
card_hash.delete_if {|key, value| value == "" || value == nil}
|
91
|
+
card_hash
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
95
|
+
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: magic_tcg_finder
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Alexandra Wright
|
@@ -104,6 +104,10 @@ extensions: []
|
|
104
104
|
extra_rdoc_files: []
|
105
105
|
files:
|
106
106
|
- bin/magic_tcg_finder
|
107
|
+
- config/environment.rb
|
108
|
+
- lib/magic_tcg_finder/card.rb
|
109
|
+
- lib/magic_tcg_finder/command_line_interface.rb
|
110
|
+
- lib/magic_tcg_finder/scraper.rb
|
107
111
|
homepage: https://github.com/f3mshep/magic_tcg_finder-cli-app
|
108
112
|
licenses:
|
109
113
|
- MIT
|
@@ -112,6 +116,7 @@ post_install_message:
|
|
112
116
|
rdoc_options: []
|
113
117
|
require_paths:
|
114
118
|
- lib
|
119
|
+
- config
|
115
120
|
required_ruby_version: !ruby/object:Gem::Requirement
|
116
121
|
requirements:
|
117
122
|
- - ">="
|
@@ -124,7 +129,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
124
129
|
version: '0'
|
125
130
|
requirements: []
|
126
131
|
rubyforge_project:
|
127
|
-
rubygems_version: 2.
|
132
|
+
rubygems_version: 2.6.11
|
128
133
|
signing_key:
|
129
134
|
specification_version: 4
|
130
135
|
summary: 'Magic: The Gathering card finder'
|