mtg-card-finder 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +69 -0
- data/Rakefile +14 -0
- data/bin/console +14 -0
- data/bin/mtg-card-finder +5 -0
- data/bin/setup +8 -0
- data/config/environment.rb +10 -0
- data/db/cards.db +0 -0
- data/lib/mtg_card_finder.rb +4 -0
- data/lib/mtg_card_finder/cli.rb +77 -0
- data/lib/mtg_card_finder/color.rb +49 -0
- data/lib/mtg_card_finder/concerns/persistable.rb +213 -0
- data/lib/mtg_card_finder/mtg.rb +118 -0
- data/lib/mtg_card_finder/parser.rb +143 -0
- data/lib/mtg_card_finder/tables/modern_fall.rb +25 -0
- data/lib/mtg_card_finder/tables/modern_rise.rb +25 -0
- data/lib/mtg_card_finder/tables/standard_fall.rb +25 -0
- data/lib/mtg_card_finder/tables/standard_rise.rb +25 -0
- data/lib/mtg_card_finder/version.rb +3 -0
- data/mtg-card-finder.gemspec +42 -0
- data/spec.md +7 -0
- metadata +195 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: fdde645b062acfb7c6bd68782007c76ec0aaa3ce
|
4
|
+
data.tar.gz: 64fed502a10a82fe590dd969773b63914310f1d3
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 0a0e8791896966c8d54bec61b4439227f85055a062f4f2078664447bb42b1ccce9b878ca41f9f130209543135748c350efb437f8f7c2f6213f44301db4a2cd91
|
7
|
+
data.tar.gz: b16f5dba901adf924d286e6d23210d14e00e42979d03199f4abb893f37ac93241101a1a2e3d11174917fd0a14b0188631c27b47902ef17e2a23f74e028f37150
|
data/.gitignore
ADDED
data/CODE_OF_CONDUCT.md
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
# Contributor Covenant Code of Conduct
|
2
|
+
|
3
|
+
## Our Pledge
|
4
|
+
|
5
|
+
In the interest of fostering an open and welcoming environment, we as
|
6
|
+
contributors and maintainers pledge to making participation in our project and
|
7
|
+
our community a harassment-free experience for everyone, regardless of age, body
|
8
|
+
size, disability, ethnicity, gender identity and expression, level of experience,
|
9
|
+
nationality, personal appearance, race, religion, or sexual identity and
|
10
|
+
orientation.
|
11
|
+
|
12
|
+
## Our Standards
|
13
|
+
|
14
|
+
Examples of behavior that contributes to creating a positive environment
|
15
|
+
include:
|
16
|
+
|
17
|
+
* Using welcoming and inclusive language
|
18
|
+
* Being respectful of differing viewpoints and experiences
|
19
|
+
* Gracefully accepting constructive criticism
|
20
|
+
* Focusing on what is best for the community
|
21
|
+
* Showing empathy towards other community members
|
22
|
+
|
23
|
+
Examples of unacceptable behavior by participants include:
|
24
|
+
|
25
|
+
* The use of sexualized language or imagery and unwelcome sexual attention or
|
26
|
+
advances
|
27
|
+
* Trolling, insulting/derogatory comments, and personal or political attacks
|
28
|
+
* Public or private harassment
|
29
|
+
* Publishing others' private information, such as a physical or electronic
|
30
|
+
address, without explicit permission
|
31
|
+
* Other conduct which could reasonably be considered inappropriate in a
|
32
|
+
professional setting
|
33
|
+
|
34
|
+
## Our Responsibilities
|
35
|
+
|
36
|
+
Project maintainers are responsible for clarifying the standards of acceptable
|
37
|
+
behavior and are expected to take appropriate and fair corrective action in
|
38
|
+
response to any instances of unacceptable behavior.
|
39
|
+
|
40
|
+
Project maintainers have the right and responsibility to remove, edit, or
|
41
|
+
reject comments, commits, code, wiki edits, issues, and other contributions
|
42
|
+
that are not aligned to this Code of Conduct, or to ban temporarily or
|
43
|
+
permanently any contributor for other behaviors that they deem inappropriate,
|
44
|
+
threatening, offensive, or harmful.
|
45
|
+
|
46
|
+
## Scope
|
47
|
+
|
48
|
+
This Code of Conduct applies both within project spaces and in public spaces
|
49
|
+
when an individual is representing the project or its community. Examples of
|
50
|
+
representing a project or community include using an official project e-mail
|
51
|
+
address, posting via an official social media account, or acting as an appointed
|
52
|
+
representative at an online or offline event. Representation of a project may be
|
53
|
+
further defined and clarified by project maintainers.
|
54
|
+
|
55
|
+
## Enforcement
|
56
|
+
|
57
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
58
|
+
reported by contacting the project team at gongora.animations@gmail.com. All
|
59
|
+
complaints will be reviewed and investigated and will result in a response that
|
60
|
+
is deemed necessary and appropriate to the circumstances. The project team is
|
61
|
+
obligated to maintain confidentiality with regard to the reporter of an incident.
|
62
|
+
Further details of specific enforcement policies may be posted separately.
|
63
|
+
|
64
|
+
Project maintainers who do not follow or enforce the Code of Conduct in good
|
65
|
+
faith may face temporary or permanent repercussions as determined by other
|
66
|
+
members of the project's leadership.
|
67
|
+
|
68
|
+
## Attribution
|
69
|
+
|
70
|
+
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
71
|
+
available at [http://contributor-covenant.org/version/1/4][version]
|
72
|
+
|
73
|
+
[homepage]: http://contributor-covenant.org
|
74
|
+
[version]: http://contributor-covenant.org/version/1/4/
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2017 Juan Gongora
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
# Mtg-Card-Finder
|
2
|
+
|
3
|
+
Welcome to MTG Card Finder! Find the highest rising/falling card prices on the Magic the Gathering open market.
|
4
|
+
Updated daily, and able to save your search into a local .csv file for your own personal logging/card hunting.
|
5
|
+
|
6
|
+
Currently logs Standard and Modern formats only.
|
7
|
+
|
8
|
+
## Installation
|
9
|
+
|
10
|
+
Add this line to your application's Gemfile:
|
11
|
+
|
12
|
+
```ruby
|
13
|
+
gem 'mtg-card-finder'
|
14
|
+
```
|
15
|
+
|
16
|
+
And then execute:
|
17
|
+
|
18
|
+
$ bundle
|
19
|
+
|
20
|
+
Or install it yourself as:
|
21
|
+
|
22
|
+
$ gem install mtg-card-finder
|
23
|
+
|
24
|
+
## Usage
|
25
|
+
Type the below on your terminal to begin the app:
|
26
|
+
|
27
|
+
$ mtg-card-finder
|
28
|
+
|
29
|
+
When the application begins, choose between four different pricing options:
|
30
|
+
|
31
|
+
[1] Standard: rising cards today
|
32
|
+
[2] Modern: rising cards today
|
33
|
+
[3] Standard: crashing cards today
|
34
|
+
[4] Modern: crashing cards today
|
35
|
+
|
36
|
+
After you have made your choice the app will load a list of the top 40-50 for the day.
|
37
|
+
|
38
|
+
Each card will display its 'name', what card 'set' it's from, the current 'market value',
|
39
|
+
and the amount that it has 'raised' or 'fallen' for the day.
|
40
|
+
|
41
|
+
You will then be asked for four more additional options:
|
42
|
+
|
43
|
+
[1] search for a different format's market?
|
44
|
+
[2] save the current card search listing into a CSV file?
|
45
|
+
[3] purchase one of the queried cards in the open market?
|
46
|
+
[4] exit the program?
|
47
|
+
|
48
|
+
Option 1 will let you search for another of the initially provided formats at the start of the application.
|
49
|
+
Option 2 will locally save the queried listing into a .csv file.
|
50
|
+
Option 3 will provide you with a url link that directs you to eBay's lowest priced bids for the chosen card.
|
51
|
+
Option 4 exits the application.
|
52
|
+
|
53
|
+
## Development
|
54
|
+
|
55
|
+
After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
56
|
+
|
57
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
58
|
+
|
59
|
+
## Contributing
|
60
|
+
|
61
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/JuanGongora/mtg-card-finder. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
62
|
+
|
63
|
+
## Bugs
|
64
|
+
|
65
|
+
Current bugs: https://github.com/JuanGongora/mtg-card-finder/issues
|
66
|
+
|
67
|
+
## License
|
68
|
+
|
69
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
2
|
+
require_relative 'config/environment' #everything is being operated from within here
|
3
|
+
|
4
|
+
task :default => :spec
|
5
|
+
|
6
|
+
task :console do #cutsom console initialization for testing
|
7
|
+
|
8
|
+
def reload! #lets me load all my files again if I make a change
|
9
|
+
load_all 'lib'
|
10
|
+
end
|
11
|
+
|
12
|
+
Pry.start #allows pry to start for me to fiddle around with all my methods
|
13
|
+
|
14
|
+
end
|
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require_relative "../lib/mtg_card_finder"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start(__FILE__)
|
data/bin/mtg-card-finder
ADDED
data/bin/setup
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
require 'open-uri'
|
2
|
+
require 'sqlite3'
|
3
|
+
require "tco"
|
4
|
+
require "mechanize"
|
5
|
+
require "nokogiri"
|
6
|
+
require "require_all"
|
7
|
+
|
8
|
+
DB = {:conn => SQLite3::Database.new("db/cards.db")} #this gives me validation to reach the database that module Persistable interacts with
|
9
|
+
|
10
|
+
require_all 'lib/mtg_card_finder' #this allows me to simultaneously require everything in lib/mtg_card_finder
|
data/db/cards.db
ADDED
File without changes
|
@@ -0,0 +1,77 @@
|
|
1
|
+
class MTGCardFinder::CLI
|
2
|
+
|
3
|
+
def self.start
|
4
|
+
Parser.reset_query_info
|
5
|
+
puts "Powered by MTG$ (mtgprice.com)"; sleep(0.5);
|
6
|
+
puts "-------------------------------------------------"
|
7
|
+
puts "Please select your price trend Format:"
|
8
|
+
puts "-------------------------------------------------"
|
9
|
+
puts "#{"|Last Update|".fg COLORS[6]}#{Parser.update_date}"
|
10
|
+
puts "-------------------------------------------------"
|
11
|
+
self.set_choosing
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.set_text
|
15
|
+
puts "#{"[1]".fg COLORS[3]} Standard: #{"rising".fg COLORS[4]} cards today"
|
16
|
+
puts "#{"[2]".fg COLORS[3]} Modern: #{"rising".fg COLORS[4]} cards today"
|
17
|
+
puts "#{"[3]".fg COLORS[3]} Standard: #{"crashing".fg COLORS[6]} cards today"
|
18
|
+
puts "#{"[4]".fg COLORS[3]} Modern: #{"crashing".fg COLORS[6]} cards today"
|
19
|
+
puts "-------------------------------------------------"
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.options_text
|
23
|
+
puts "Would you like to?"
|
24
|
+
puts "#{"[1]".fg COLORS[3]} search for a different format's market?"
|
25
|
+
puts "#{"[2]".fg COLORS[3]} save the current card search listing into a CSV file?"
|
26
|
+
puts "#{"[3]".fg COLORS[3]} purchase one of the queried cards in the open market?"
|
27
|
+
puts "#{"[4]".fg COLORS[3]} exit the program?"
|
28
|
+
puts "-------------------------------------------------"
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.set_choosing
|
32
|
+
self.set_text
|
33
|
+
self.set_input
|
34
|
+
Parser.scrape_cards
|
35
|
+
Parser.display_cards
|
36
|
+
puts ""
|
37
|
+
puts "-------------------------------------------------"
|
38
|
+
puts ""
|
39
|
+
self.options_text
|
40
|
+
self.options_input
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.set_input
|
44
|
+
sleep(1)
|
45
|
+
puts "Please type out the #{"number".fg COLORS[3]} of the format you would like to see from above..."
|
46
|
+
Parser.select_format
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.options_input
|
50
|
+
input = gets.strip.to_i
|
51
|
+
if input == 1
|
52
|
+
puts "Please select your price trend Format:"
|
53
|
+
self.set_choosing
|
54
|
+
elsif input == 2
|
55
|
+
Parser.csv
|
56
|
+
sleep(2)
|
57
|
+
self.options_text
|
58
|
+
self.options_input
|
59
|
+
elsif input == 3
|
60
|
+
puts "Please type out the #{"number".fg COLORS[4]} from one of the above searched cards:"
|
61
|
+
Parser.purchase
|
62
|
+
sleep(2.5)
|
63
|
+
self.options_text
|
64
|
+
self.options_input
|
65
|
+
elsif input == 4
|
66
|
+
puts ""
|
67
|
+
puts ""
|
68
|
+
puts "Thank you for using #{"MTG".fg COLORS[6]} #{"CARD".fg COLORS[5]} #{"FINDER".fg COLORS[4]}"
|
69
|
+
puts "-----------------------------------"
|
70
|
+
exit
|
71
|
+
else
|
72
|
+
puts "That is not a valid option"
|
73
|
+
self.options_input
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
#creating color configurations for the gem tco
|
2
|
+
conf = Tco.config
|
3
|
+
conf.names["purple"] = "#622e90"
|
4
|
+
conf.names["dark-blue"] = "#2d3091"
|
5
|
+
conf.names["blue"] = "#42cbff"
|
6
|
+
conf.names["green"] = "#59ff00"
|
7
|
+
conf.names["yellow"] = "#fdea22"
|
8
|
+
conf.names["orange"] = "#f37f5a"
|
9
|
+
conf.names["red"] = "#ff476c"
|
10
|
+
conf.names["light_purp"] = "#4d5a75"
|
11
|
+
Tco.reconfigure conf
|
12
|
+
|
13
|
+
#supplementing the color configurations into an array constant
|
14
|
+
COLORS = ["purple", "dark-blue", "blue", "green", "yellow", "orange", "red", "light_purp"]
|
15
|
+
|
16
|
+
mtg = <<-EOS
|
17
|
+
BB BB BBBBBBBBB BBBBBBBBB
|
18
|
+
BBBB BBBB BBB BBBB BBB
|
19
|
+
BBBBBBBBBBBBBB BBB BBB
|
20
|
+
BBBB BB BBBB BBB BBB BBBBBB
|
21
|
+
BBB BBB BBB BBBB BBB
|
22
|
+
BBBBBB BBBBBB BBB BBBBBBBBBBB
|
23
|
+
EOS
|
24
|
+
|
25
|
+
card = <<-EOS
|
26
|
+
|
27
|
+
BBBBBBB BBBB BBBBBBB BBBBBBB
|
28
|
+
BBB BB BB BB BBB BB BBB
|
29
|
+
BBB BBB BBB BB BBB BB BBB
|
30
|
+
BBB BBBBBBBBBB BBBBBB BB BBB
|
31
|
+
BBB BBB BBBB BB BBB BB BBB
|
32
|
+
BBBBBB BBB BBBB BB BBB BBBBBBB
|
33
|
+
EOS
|
34
|
+
|
35
|
+
finder = <<-EOS
|
36
|
+
|
37
|
+
BBBBBBBBBB BB BBB BB BBBBBBB BBBBBBB BBBBBBB
|
38
|
+
BB BB BB BB BB BB BBB BB BB BBB
|
39
|
+
BBBBBBBBB BB BB BB BB BB BBB BBBBB BBBBBB
|
40
|
+
BB BB BB BB BB BB BBB BB BB BB
|
41
|
+
BB BB BB BB BB BB BBB BB BB BB
|
42
|
+
BB BB BB BBBB BBBBBBBB BBBBBBB BB BB
|
43
|
+
EOS
|
44
|
+
|
45
|
+
puts ""
|
46
|
+
print mtg.fg "red"; sleep(1);
|
47
|
+
print card.fg "orange"; sleep(1);
|
48
|
+
print finder.fg "yellow"; sleep(1);
|
49
|
+
puts ""
|
@@ -0,0 +1,213 @@
|
|
1
|
+
#a dynamic module that contains data that can be re-used, hence the name
|
2
|
+
module Persistable
|
3
|
+
|
4
|
+
module ClassMethods
|
5
|
+
|
6
|
+
def table_name
|
7
|
+
"#{self.to_s.downcase}s"
|
8
|
+
end
|
9
|
+
|
10
|
+
#this method will dynamically create a new instance with the assigned attrs and values
|
11
|
+
#by doing mass assignment of the hash's key/value pairs
|
12
|
+
def create(attributes_hash)
|
13
|
+
#the tap method allows preconfigured methods and values to
|
14
|
+
#be associated with the instance during instantiation while also automatically returning
|
15
|
+
#the object after its creation is concluded.
|
16
|
+
self.new.tap do |card|
|
17
|
+
attributes_hash.each do |key, value|
|
18
|
+
#sending the new instance the key name as a setter with the value
|
19
|
+
card.send("#{key}=", value)
|
20
|
+
#string interpolation is used as the method doesn't know the key name yet
|
21
|
+
#but an = sign is implemented into the string in order to asssociate it as a setter
|
22
|
+
end
|
23
|
+
#saves the new instance into the database
|
24
|
+
card.save
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def create_table
|
29
|
+
sql = <<-SQL
|
30
|
+
CREATE TABLE IF NOT EXISTS #{self.table_name} ( #{self.create_sql} )
|
31
|
+
SQL
|
32
|
+
|
33
|
+
DB[:conn].execute(sql)
|
34
|
+
end
|
35
|
+
|
36
|
+
def remove_table
|
37
|
+
sql = <<-SQL
|
38
|
+
DROP TABLE IF EXISTS #{self.table_name}
|
39
|
+
SQL
|
40
|
+
|
41
|
+
DB[:conn].execute(sql)
|
42
|
+
end
|
43
|
+
|
44
|
+
def table_empty?
|
45
|
+
sql = <<-SQL
|
46
|
+
SELECT * FROM #{self.table_name}
|
47
|
+
SQL
|
48
|
+
|
49
|
+
table = DB[:conn].execute(sql)
|
50
|
+
check = table.empty?
|
51
|
+
if check == true
|
52
|
+
true
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def table_rows
|
57
|
+
sql = <<-SQL
|
58
|
+
SELECT COUNT(*) FROM #{self.table_name}
|
59
|
+
SQL
|
60
|
+
|
61
|
+
table = DB[:conn].execute(sql)
|
62
|
+
rows = table.flatten.join.to_i
|
63
|
+
rows
|
64
|
+
end
|
65
|
+
|
66
|
+
def make_csv_file
|
67
|
+
#collects everything in sql table
|
68
|
+
rows = DB[:conn].execute("SELECT * FROM #{self.table_name}")
|
69
|
+
date = "#{Time.now}"[0..9].gsub!("-", "_")
|
70
|
+
#naming the csv file with today's date
|
71
|
+
fname = "#{self}_#{date}.csv"
|
72
|
+
#collecting the table's column names
|
73
|
+
col_names = "#{self.attributes.keys.join(", ")} \n"
|
74
|
+
unless File.exists? fname
|
75
|
+
#opening the csv file to write data into
|
76
|
+
File.open(fname, 'w') do |ofile|
|
77
|
+
#first row will be column names
|
78
|
+
ofile << col_names
|
79
|
+
rows.each_with_index do |value, index|
|
80
|
+
#iterates through all the rows values to replace commas so as to avoid line breaking errors
|
81
|
+
value.each { |find| find.gsub!(", ", "_") if find.is_a?(String) }
|
82
|
+
#pushing each array within rows as a newline into csv while removing nil values
|
83
|
+
ofile << "#{rows[index].compact.join(", ")} \n"
|
84
|
+
end
|
85
|
+
sleep(1 + rand)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def buy_link(id)
|
91
|
+
name = self.find(id)
|
92
|
+
begin
|
93
|
+
#collect the instant's values as a string
|
94
|
+
word = name.card + " " + name.sets
|
95
|
+
rescue
|
96
|
+
#instead of getting an undefined method error in .card & .sets for nil:NilClass
|
97
|
+
#just re-run method until user sets it to a true value
|
98
|
+
Parser.purchase
|
99
|
+
else
|
100
|
+
#replace whitespace chars
|
101
|
+
word.gsub!(/\s+/m, '%20')
|
102
|
+
#create url for purchasing the chosen id card
|
103
|
+
buy = "http://www.ebay.com/sch/?_nkw=#{word}&_sacat=0".fg COLORS[3]
|
104
|
+
puts ""
|
105
|
+
puts "Please highlight, right click and copy the #{"url".fg COLORS[3]} below and paste it to your preferred browser:"
|
106
|
+
puts "--------------------------------------------------------------------------------------------"
|
107
|
+
puts ""
|
108
|
+
puts buy
|
109
|
+
puts ""
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def find(id)
|
114
|
+
sql = <<-SQL
|
115
|
+
SELECT * FROM #{self.table_name} WHERE id=(?)
|
116
|
+
SQL
|
117
|
+
|
118
|
+
row = DB[:conn].execute(sql, id)
|
119
|
+
#if a row is actually returned i.e. the id actually exists
|
120
|
+
if row.first
|
121
|
+
self.reify_from_row(row.first)
|
122
|
+
#using .first array method to return only the first nested array
|
123
|
+
#that is taken from self.reify_from_row(row) which is the resulting id of the query
|
124
|
+
else
|
125
|
+
puts "This card does not exist"
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
#opposite of abstraction is reification i.e. I'm getting the raw data of these variables
|
130
|
+
def reify_from_row(row)
|
131
|
+
#the tap method allows preconfigured methods and values to
|
132
|
+
#be associated with the instance during instantiation while also automatically returning
|
133
|
+
#the object after its creation is concluded.
|
134
|
+
self.new.tap do |card|
|
135
|
+
self.attributes.keys.each.with_index do |key, index|
|
136
|
+
#sending the new instance the key name as a setter with the value located at the 'row' index
|
137
|
+
card.send("#{key}=", row[index])
|
138
|
+
#string interpolation is used as the method doesn't know the key name yet
|
139
|
+
#but an = sign is implemented into the string in order to asssociate it as a setter
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
def create_sql
|
145
|
+
#will apply the column names ('key') and their schemas ('value') into sql strings without having to hard code them
|
146
|
+
#the collect method returns the revised array and then we concatenate it into a string separating the contents with a comma
|
147
|
+
self.attributes.collect {|key, value| "#{key} #{value}"}.join(", ")
|
148
|
+
end
|
149
|
+
|
150
|
+
def attributes_names_insert_sql
|
151
|
+
#same idea as self.create_sql only it's returning the 'key' for sql insertions
|
152
|
+
self.attributes.keys[1..-1].join(", ")
|
153
|
+
end
|
154
|
+
|
155
|
+
def question_marks_insert_sql
|
156
|
+
#returns the number of key-value pairs in the hash minus one for the 'id'
|
157
|
+
questions = self.attributes.keys.size-1
|
158
|
+
#converts them into '?' array that is then turned into comma separated string
|
159
|
+
questions.times.collect {"?"}.join(", ")
|
160
|
+
end
|
161
|
+
|
162
|
+
def sql_columns_to_update
|
163
|
+
#returns the number of keys in the hash minus one for the 'id'
|
164
|
+
columns = self.attributes.keys[1..-1]
|
165
|
+
#converts them into 'attribute=(?)' array that is then turned into comma separated string
|
166
|
+
columns.collect {|attr| "#{attr}=(?)"}.join(", ")
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
module InstanceMethods
|
171
|
+
|
172
|
+
def save
|
173
|
+
#if the card has already been saved, then call update method
|
174
|
+
persisted? ? update : insert
|
175
|
+
#if not call insert method instead
|
176
|
+
end
|
177
|
+
|
178
|
+
def attribute_values_for_sql_check
|
179
|
+
self.class.attributes.keys[1..-1].collect {|attr_names| self.send(attr_names)}
|
180
|
+
#I go through the key names (minus 'id') and return an array containing their values for the recieving instance
|
181
|
+
#basically like getting an array of getter methods for that instance
|
182
|
+
end
|
183
|
+
|
184
|
+
def persisted?
|
185
|
+
#the '!!' double bang converts object into a truthy value statement
|
186
|
+
!!self.id
|
187
|
+
end
|
188
|
+
|
189
|
+
def update
|
190
|
+
#updates by the unique identifier of 'id'
|
191
|
+
sql = <<-SQL
|
192
|
+
UPDATE #{self.class.table_name} SET #{self.class.sql_columns_to_update} WHERE id=(?)
|
193
|
+
SQL
|
194
|
+
|
195
|
+
#using splat operator to signify that there may be more than one argument in terms of attr_readers
|
196
|
+
DB[:conn].execute(sql, *attribute_values_for_sql_check, self.id)
|
197
|
+
end
|
198
|
+
|
199
|
+
def insert
|
200
|
+
sql = <<-SQL
|
201
|
+
INSERT INTO #{self.class.table_name} (#{self.class.attributes_names_insert_sql}) VALUES (#{self.class.question_marks_insert_sql})
|
202
|
+
SQL
|
203
|
+
|
204
|
+
#using splat operator to signify that there may be more than one argument in terms of attr_readers
|
205
|
+
DB[:conn].execute(sql, *attribute_values_for_sql_check)
|
206
|
+
#after inserting the card to the database, I want to get the primary key that is auto assigned to it
|
207
|
+
#from sql and set it to the instance method 'id' of this very instance variable.
|
208
|
+
self.id = DB[:conn].execute("SELECT last_insert_rowid() FROM #{self.class.table_name}")[0][0]
|
209
|
+
#returns first array with the first value of the array (i.e. index 0)
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
class MTG
|
2
|
+
attr_accessor :card, :sets, :market_price, :price_fluctuate, :image
|
3
|
+
@@modern_up = []
|
4
|
+
@@modern_down = []
|
5
|
+
@@standard_up = []
|
6
|
+
@@standard_down = []
|
7
|
+
@@temp_array = []
|
8
|
+
|
9
|
+
ATTRIBUTES = [
|
10
|
+
"Card:",
|
11
|
+
"Set:",
|
12
|
+
"Market Value:",
|
13
|
+
"Rise/Fall amount:",
|
14
|
+
"Image URL:"
|
15
|
+
]
|
16
|
+
|
17
|
+
#new instance will be created with already assigned values to MTG attrs
|
18
|
+
def initialize(attributes)
|
19
|
+
attributes.each {|key, value| self.send("#{key}=", value)}
|
20
|
+
end
|
21
|
+
|
22
|
+
def save_modern_up
|
23
|
+
@@modern_up << self
|
24
|
+
end
|
25
|
+
|
26
|
+
def save_modern_down
|
27
|
+
@@modern_down << self
|
28
|
+
end
|
29
|
+
|
30
|
+
def save_standard_up
|
31
|
+
@@standard_up << self
|
32
|
+
end
|
33
|
+
|
34
|
+
def save_standard_down
|
35
|
+
@@standard_down << self
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.create_modern_up(attributes)
|
39
|
+
#allows cards instance to auto return thanks to tap implementation
|
40
|
+
cards = MTG.new(attributes).tap {|card| card.save_modern_up}
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.create_modern_down(attributes)
|
44
|
+
cards = MTG.new(attributes).tap {|card| card.save_modern_down}
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.create_standard_up(attributes)
|
48
|
+
cards = MTG.new(attributes).tap {|card| card.save_standard_up}
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.create_standard_down(attributes)
|
52
|
+
cards = MTG.new(attributes).tap {|card| card.save_standard_down}
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.all(format)
|
56
|
+
#iterate through each instance that was appended into class variable during initialization
|
57
|
+
format.each_with_index do |card, number|
|
58
|
+
puts ""
|
59
|
+
puts "|- #{number + 1} -|".fg COLORS[4]
|
60
|
+
puts ""
|
61
|
+
#line below helps resolve glitch that allows 'ghost/invalid' cards to be selected from Parser.purchase
|
62
|
+
if number < Parser.table_length
|
63
|
+
#iterate through each instance method that was defined for the stored instance variable
|
64
|
+
card.instance_variables.each_with_index do |value, index|
|
65
|
+
#returns the value of the instance method applied to the instance
|
66
|
+
#with an index value of the first/last, key/value pairs ordered in Parser.scrape_cards
|
67
|
+
#associates a named definition of the values by titling it from constant ATTRIBUTES
|
68
|
+
if index < 4
|
69
|
+
puts "#{ATTRIBUTES[index].fg COLORS[2]} #{card.instance_variable_get(value)}"
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
puts ""
|
74
|
+
print " ".bg COLORS[7]
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
#hack that resolves glitch that would display duplicate
|
79
|
+
#recursions in the selected cards to show by user request in CLI.set_input
|
80
|
+
def self.store_temp_array(array)
|
81
|
+
@@temp_array = array
|
82
|
+
self.all(@@temp_array)
|
83
|
+
@@temp_array.clear
|
84
|
+
end
|
85
|
+
|
86
|
+
def self.search_modern_up
|
87
|
+
self.all(@@modern_up)
|
88
|
+
end
|
89
|
+
|
90
|
+
def self.search_modern_down
|
91
|
+
self.all(@@modern_down)
|
92
|
+
end
|
93
|
+
|
94
|
+
def self.search_standard_up
|
95
|
+
self.all(@@standard_up)
|
96
|
+
end
|
97
|
+
|
98
|
+
def self.search_standard_down
|
99
|
+
self.all(@@standard_down)
|
100
|
+
end
|
101
|
+
|
102
|
+
def self.modern_up
|
103
|
+
@@modern_up
|
104
|
+
end
|
105
|
+
|
106
|
+
def self.modern_down
|
107
|
+
@@modern_down
|
108
|
+
end
|
109
|
+
|
110
|
+
def self.standard_up
|
111
|
+
@@standard_up
|
112
|
+
end
|
113
|
+
|
114
|
+
def self.standard_down
|
115
|
+
@@standard_down
|
116
|
+
end
|
117
|
+
|
118
|
+
end
|
@@ -0,0 +1,143 @@
|
|
1
|
+
class Parser
|
2
|
+
@@overall_card_rows = nil
|
3
|
+
@@overall_format_options = []
|
4
|
+
@@time_review = []
|
5
|
+
|
6
|
+
def self.scrape_cards
|
7
|
+
self.card_counter
|
8
|
+
#checks if the class variable array for the MTG class is empty or not
|
9
|
+
if @@overall_format_options[9].call.empty? == false
|
10
|
+
#if user has already parsed the same content before, then scrape the locally stored variables instead of the website
|
11
|
+
MTG.store_temp_array(@@overall_format_options[9].call)
|
12
|
+
else @@overall_format_options[9].call.empty? == true
|
13
|
+
#creates a new sql table for gathering the 'to be' scraped content
|
14
|
+
@@overall_format_options[5].call
|
15
|
+
#exception handling block implemented for possible errors (i.e. website is down)
|
16
|
+
retries = 3
|
17
|
+
#'retries' will hold the amount of attempts done to block until it quits if a scraping error is not resolved
|
18
|
+
begin
|
19
|
+
agent = Mechanize.new
|
20
|
+
#'agent' will help me to make the website identify what type of user is accessing the content
|
21
|
+
#I also want to have the site understand the request that referred me to the page
|
22
|
+
agent.pre_connect_hooks << lambda do |agent, request|
|
23
|
+
agent.user_agent = "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36"
|
24
|
+
request["Referer"] = "http://www.mtgprice.com/"
|
25
|
+
end
|
26
|
+
#browser parsing begins in local variable 'doc'
|
27
|
+
doc = agent.get "http://www.mtgprice.com/taneLayout/mtg_price_tracker.jsp?period=DAILY"
|
28
|
+
rescue StandardError=>e
|
29
|
+
puts "Error: #{e}"
|
30
|
+
#implementing additonal retry to see if a resolution can be attained by reconnecting to site
|
31
|
+
if retries > 0
|
32
|
+
puts "Trying #{retries} more times"
|
33
|
+
retries -= 1
|
34
|
+
sleep 1
|
35
|
+
retry
|
36
|
+
else
|
37
|
+
puts "Unable to resolve #{e}"
|
38
|
+
end
|
39
|
+
else
|
40
|
+
#if no error was attained from 'begin' of block till now, then the card scraping commences
|
41
|
+
doc.css(@@overall_format_options[0]).each do |row|
|
42
|
+
#parsing is now initialized into MTG class, with key/value pairs for its scraped attributes
|
43
|
+
row = self.parser_format(hash = {
|
44
|
+
card: row.css(".card a")[0].text,
|
45
|
+
sets: row.css(".set a")[0].text,
|
46
|
+
market_price: row.css(".value")[0].text.split[0].gsub!("$", "").to_f,
|
47
|
+
price_fluctuate: row.css("td:last-child").text,
|
48
|
+
image: Nokogiri::HTML(open("http://www.mtgprice.com#{row.css(".card a").attribute("href").value}")).css(".card-img img").attribute("src").value
|
49
|
+
# ^^ had to go another level deep to access a better quality image from its full product listing
|
50
|
+
})
|
51
|
+
#since a stored method in an array can't have a locally passed argument I compromised by just having the class name passed from the class variable array to the method
|
52
|
+
#I also make sure that no duplicate information may be transferred into the table by comparing the card row/number count
|
53
|
+
@@overall_format_options[6].create(hash) if @@overall_format_options[6].table_rows < self.table_length
|
54
|
+
end
|
55
|
+
ensure
|
56
|
+
sleep(0.3)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.card_counter
|
62
|
+
@@overall_card_rows = nil
|
63
|
+
#shows how many rows(amount of cards) there are in total for the page as a constructed array
|
64
|
+
rows = Nokogiri::HTML(open("http://www.mtgprice.com/taneLayout/mtg_price_tracker.jsp?period=DAILY")).css(@@overall_format_options[0])[0..-1]
|
65
|
+
@@overall_card_rows = "#{rows.length}".to_i
|
66
|
+
puts "loading the #{@@overall_format_options[1]} #{@@overall_card_rows} #{@@overall_format_options[2]} #{@@overall_format_options[3]} on the market for today..."; sleep(1);
|
67
|
+
print "Please be patient"; print "."; sleep(1); print "."; sleep(1); print "."; sleep(1); print "."; sleep(1);
|
68
|
+
puts ""
|
69
|
+
puts ""
|
70
|
+
puts "-------------------------------------------------"
|
71
|
+
puts ""
|
72
|
+
puts " ".bg COLORS[7]
|
73
|
+
end
|
74
|
+
|
75
|
+
def self.select_format
|
76
|
+
@@overall_format_options.clear
|
77
|
+
input = gets.strip.to_i
|
78
|
+
case input
|
79
|
+
when 1
|
80
|
+
#the methods at the end of these arrays are stored references that can be called externally with the .call method #=> http://stackoverflow.com/questions/13948910/ruby-methods-as-array-elements-how-do-they-work
|
81
|
+
@@overall_format_options = ["#top50Standard tr", "top", "Standard", "#{"gainers".fg COLORS[4]}", StandardRise.method(:remove_table), StandardRise.method(:create_table), StandardRise, StandardRise.method(:make_csv_file), MTG.method(:search_standard_up), MTG.method(:standard_up)]
|
82
|
+
when 2
|
83
|
+
@@overall_format_options = ["#top50Modern tr", "top", "Modern", "#{"gainers".fg COLORS[4]}", ModernRise.method(:remove_table), ModernRise.method(:create_table), ModernRise, ModernRise.method(:make_csv_file), MTG.method(:search_modern_up), MTG.method(:modern_up)]
|
84
|
+
when 3
|
85
|
+
@@overall_format_options = ["#bottom50Standard tr", "bottom", "Standard", "#{"crashers".fg COLORS[6]}", StandardFall.method(:remove_table), StandardFall.method(:create_table), StandardFall, StandardFall.method(:make_csv_file), MTG.method(:search_standard_down), MTG.method(:standard_down)]
|
86
|
+
when 4
|
87
|
+
@@overall_format_options = ["#bottom50Modern tr", "bottom", "Modern", "#{"crashers".fg COLORS[6]}", ModernFall.method(:remove_table), ModernFall.method(:create_table), ModernFall, ModernFall.method(:make_csv_file), MTG.method(:search_modern_down), MTG.method(:modern_down)]
|
88
|
+
else
|
89
|
+
CLI.set_input
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
#used within self.scrape_cards, it assists with the assigning of instances to the preferred class variable in MTG
|
94
|
+
def self.parser_format(attributes)
|
95
|
+
if self.format_name == "StandardRise"
|
96
|
+
MTG.create_standard_up(attributes)
|
97
|
+
elsif self.format_name == "ModernRise"
|
98
|
+
MTG.create_modern_up(attributes)
|
99
|
+
elsif self.format_name == "StandardFall"
|
100
|
+
MTG.create_standard_down(attributes)
|
101
|
+
else self.format_name == "ModernFall"
|
102
|
+
MTG.create_modern_down(attributes)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def self.display_cards
|
107
|
+
@@overall_format_options[8].call
|
108
|
+
end
|
109
|
+
|
110
|
+
def self.format_name
|
111
|
+
"#{@@overall_format_options[6]}"
|
112
|
+
end
|
113
|
+
|
114
|
+
def self.table_length
|
115
|
+
@@overall_card_rows
|
116
|
+
end
|
117
|
+
|
118
|
+
def self.purchase
|
119
|
+
input = gets.strip.to_i
|
120
|
+
@@overall_format_options[6].buy_link(input)
|
121
|
+
end
|
122
|
+
|
123
|
+
def self.csv
|
124
|
+
#Klass.make_csv_file
|
125
|
+
@@overall_format_options[7].call
|
126
|
+
puts ""
|
127
|
+
puts "The #{"CSV".fg COLORS[3]} file has been saved to your hard disk"
|
128
|
+
puts "---------------------------------------------"
|
129
|
+
puts ""
|
130
|
+
end
|
131
|
+
|
132
|
+
def self.update_date
|
133
|
+
time = Nokogiri::HTML(open("http://www.mtgprice.com/taneLayout/mtg_price_tracker.jsp?period=DAILY"))
|
134
|
+
time.css(".span6 h3")[0].text.split.join(" ").gsub!("Updated:", "")
|
135
|
+
end
|
136
|
+
|
137
|
+
# used to clear the tables so that the next re-run will have new, updated content from the website
|
138
|
+
def self.reset_query_info
|
139
|
+
@@time_review = [StandardRise.method(:remove_table), ModernRise.method(:remove_table), StandardFall.method(:remove_table), ModernFall.method(:remove_table)]
|
140
|
+
@@time_review.each_with_index {|method, index| @@time_review[index].call}
|
141
|
+
end
|
142
|
+
|
143
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require_relative '../concerns/persistable'
|
2
|
+
class ModernFall
|
3
|
+
include Persistable::InstanceMethods
|
4
|
+
extend Persistable::ClassMethods
|
5
|
+
|
6
|
+
#metaprogramming the hash to convert keys to attr_accessor's and also for inserting the values to the sql strings
|
7
|
+
ATTRS = {
|
8
|
+
:id => "INTEGER PRIMARY KEY",
|
9
|
+
:card => "TEXT",
|
10
|
+
:sets => "TEXT",
|
11
|
+
:market_price => "INTEGER",
|
12
|
+
:price_fluctuate => "TEXT",
|
13
|
+
:image => "TEXT"
|
14
|
+
}
|
15
|
+
|
16
|
+
#reader that can be accessed by Persistable module to know the unique class's constant
|
17
|
+
def self.attributes
|
18
|
+
ATTRS
|
19
|
+
end
|
20
|
+
|
21
|
+
#abstracting the collection of keys into attributes
|
22
|
+
self.attributes.keys.each do |key|
|
23
|
+
attr_accessor key
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require_relative '../concerns/persistable'
|
2
|
+
class ModernRise
|
3
|
+
include Persistable::InstanceMethods
|
4
|
+
extend Persistable::ClassMethods
|
5
|
+
|
6
|
+
#metaprogramming the hash to convert keys to attr_accessor's and also for inserting the values to the sql strings
|
7
|
+
ATTRS = {
|
8
|
+
:id => "INTEGER PRIMARY KEY",
|
9
|
+
:card => "TEXT",
|
10
|
+
:sets => "TEXT",
|
11
|
+
:market_price => "INTEGER",
|
12
|
+
:price_fluctuate => "TEXT",
|
13
|
+
:image => "TEXT"
|
14
|
+
}
|
15
|
+
|
16
|
+
#reader that can be accessed by Persistable module to know the unique class's constant
|
17
|
+
def self.attributes
|
18
|
+
ATTRS
|
19
|
+
end
|
20
|
+
|
21
|
+
#abstracting the collection of keys into attributes
|
22
|
+
self.attributes.keys.each do |key|
|
23
|
+
attr_accessor key
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require_relative '../concerns/persistable'
|
2
|
+
class StandardFall
|
3
|
+
include Persistable::InstanceMethods
|
4
|
+
extend Persistable::ClassMethods
|
5
|
+
|
6
|
+
#metaprogramming the hash to convert keys to attr_accessor's and also for inserting the values to the sql strings
|
7
|
+
ATTRS = {
|
8
|
+
:id => "INTEGER PRIMARY KEY",
|
9
|
+
:card => "TEXT",
|
10
|
+
:sets => "TEXT",
|
11
|
+
:market_price => "INTEGER",
|
12
|
+
:price_fluctuate => "TEXT",
|
13
|
+
:image => "TEXT"
|
14
|
+
}
|
15
|
+
|
16
|
+
#reader that can be accessed by Persistable module to know the unique class's constant
|
17
|
+
def self.attributes
|
18
|
+
ATTRS
|
19
|
+
end
|
20
|
+
|
21
|
+
#abstracting the collection of keys into attributes
|
22
|
+
self.attributes.keys.each do |key|
|
23
|
+
attr_accessor key
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require_relative '../concerns/persistable'
|
2
|
+
class StandardRise
|
3
|
+
include Persistable::InstanceMethods
|
4
|
+
extend Persistable::ClassMethods
|
5
|
+
|
6
|
+
#metaprogramming the hash to convert keys to attr_accessor's and also for inserting the values to the sql strings
|
7
|
+
ATTRS = {
|
8
|
+
:id => "INTEGER PRIMARY KEY",
|
9
|
+
:card => "TEXT",
|
10
|
+
:sets => "TEXT",
|
11
|
+
:market_price => "INTEGER",
|
12
|
+
:price_fluctuate => "TEXT",
|
13
|
+
:image => "TEXT"
|
14
|
+
}
|
15
|
+
|
16
|
+
#reader that can be accessed by Persistable module to know the unique class's constant
|
17
|
+
def self.attributes
|
18
|
+
ATTRS
|
19
|
+
end
|
20
|
+
|
21
|
+
#abstracting the collection of keys into attributes
|
22
|
+
self.attributes.keys.each do |key|
|
23
|
+
attr_accessor key
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'mtg_card_finder/version.rb'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "mtg-card-finder"
|
8
|
+
spec.version = MTGCardFinder::VERSION
|
9
|
+
spec.authors = ["Juan Gongora"]
|
10
|
+
spec.email = ["gongora.animations@gmail.com"]
|
11
|
+
|
12
|
+
spec.summary = %q{Daily market analyzer for MTG cards}
|
13
|
+
spec.description = %q{Find the highest rising/falling card prices on the MTG open market}
|
14
|
+
spec.homepage = "https://github.com/JuanGongora/mtg-card-finder"
|
15
|
+
spec.license = "MIT"
|
16
|
+
|
17
|
+
# Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
|
18
|
+
# to allow pushing to a single host or delete this section to allow pushing to any host.
|
19
|
+
# if spec.respond_to?(:metadata)
|
20
|
+
# spec.metadata['allowed_push_host'] = "Set to 'http://mygemserver.com'"
|
21
|
+
# else
|
22
|
+
# raise "RubyGems 2.0 or newer is required to protect against " \
|
23
|
+
# "public gem pushes."
|
24
|
+
# end
|
25
|
+
|
26
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
27
|
+
f.match(%r{^(test|spec|features)/})
|
28
|
+
end
|
29
|
+
spec.bindir = "exe"
|
30
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
31
|
+
spec.require_paths = ["lib"]
|
32
|
+
|
33
|
+
spec.add_development_dependency "bundler", "~> 1.14"
|
34
|
+
spec.add_development_dependency "rake", "~> 12.0"
|
35
|
+
spec.add_development_dependency "pry", "~> 0.10.4"
|
36
|
+
spec.add_dependency "rubysl-open-uri", "~> 2.0"
|
37
|
+
spec.add_dependency "nokogiri", "~> 1.7.1"
|
38
|
+
spec.add_dependency "tco", "~> 0.1.8"
|
39
|
+
spec.add_dependency "sqlite3", "~> 1.3.13"
|
40
|
+
spec.add_dependency "mechanize", "~> 2.7.5"
|
41
|
+
spec.add_dependency "require_all", "~> 1.4"
|
42
|
+
end
|
data/spec.md
ADDED
metadata
ADDED
@@ -0,0 +1,195 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: mtg-card-finder
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Juan Gongora
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-04-22 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.14'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.14'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '12.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '12.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: pry
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 0.10.4
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 0.10.4
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rubysl-open-uri
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '2.0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '2.0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: nokogiri
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 1.7.1
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 1.7.1
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: tco
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 0.1.8
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: 0.1.8
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: sqlite3
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: 1.3.13
|
104
|
+
type: :runtime
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: 1.3.13
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: mechanize
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - "~>"
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: 2.7.5
|
118
|
+
type: :runtime
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - "~>"
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: 2.7.5
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: require_all
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - "~>"
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '1.4'
|
132
|
+
type: :runtime
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - "~>"
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '1.4'
|
139
|
+
description: Find the highest rising/falling card prices on the MTG open market
|
140
|
+
email:
|
141
|
+
- gongora.animations@gmail.com
|
142
|
+
executables: []
|
143
|
+
extensions: []
|
144
|
+
extra_rdoc_files: []
|
145
|
+
files:
|
146
|
+
- ".gitignore"
|
147
|
+
- CODE_OF_CONDUCT.md
|
148
|
+
- Gemfile
|
149
|
+
- Gemfile.lock
|
150
|
+
- LICENSE.txt
|
151
|
+
- README.md
|
152
|
+
- Rakefile
|
153
|
+
- bin/console
|
154
|
+
- bin/mtg-card-finder
|
155
|
+
- bin/setup
|
156
|
+
- config/environment.rb
|
157
|
+
- db/cards.db
|
158
|
+
- lib/mtg_card_finder.rb
|
159
|
+
- lib/mtg_card_finder/cli.rb
|
160
|
+
- lib/mtg_card_finder/color.rb
|
161
|
+
- lib/mtg_card_finder/concerns/persistable.rb
|
162
|
+
- lib/mtg_card_finder/mtg.rb
|
163
|
+
- lib/mtg_card_finder/parser.rb
|
164
|
+
- lib/mtg_card_finder/tables/modern_fall.rb
|
165
|
+
- lib/mtg_card_finder/tables/modern_rise.rb
|
166
|
+
- lib/mtg_card_finder/tables/standard_fall.rb
|
167
|
+
- lib/mtg_card_finder/tables/standard_rise.rb
|
168
|
+
- lib/mtg_card_finder/version.rb
|
169
|
+
- mtg-card-finder.gemspec
|
170
|
+
- spec.md
|
171
|
+
homepage: https://github.com/JuanGongora/mtg-card-finder
|
172
|
+
licenses:
|
173
|
+
- MIT
|
174
|
+
metadata: {}
|
175
|
+
post_install_message:
|
176
|
+
rdoc_options: []
|
177
|
+
require_paths:
|
178
|
+
- lib
|
179
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
180
|
+
requirements:
|
181
|
+
- - ">="
|
182
|
+
- !ruby/object:Gem::Version
|
183
|
+
version: '0'
|
184
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
185
|
+
requirements:
|
186
|
+
- - ">="
|
187
|
+
- !ruby/object:Gem::Version
|
188
|
+
version: '0'
|
189
|
+
requirements: []
|
190
|
+
rubyforge_project:
|
191
|
+
rubygems_version: 2.6.11
|
192
|
+
signing_key:
|
193
|
+
specification_version: 4
|
194
|
+
summary: Daily market analyzer for MTG cards
|
195
|
+
test_files: []
|