module MtgDb

Top level module

require 'logger' require 'pry'

Constants

ALL_CARDS_DIR
DOUBLE_FACED_DIR
SCHEMA_FILENAME
SQLITE3_HEADER_STRING
SQLITE3_HEADER_STRING_LENGTH
TMP_DIR
VERSION

Attributes

standard_files_downloaded[RW]

Public Class Methods

add_all_cards_to_db(db_filename, tmp_dir) click to toggle source
# File lib/mtg_db.rb, line 41
def self.add_all_cards_to_db(db_filename, tmp_dir)
  # Connect to db
  db = Sequel.sqlite(db_filename)
  # db.loggers << Logger.new($stdout)
  db.run 'PRAGMA synchronous = 0'
  require 'mtg_db/models'

  files = @standard_files_downloaded
  if files.nil? or files.empty?
    tmp_dir ||= TMP_DIR
    tmp_dir = File.join(tmp_dir, ALL_CARDS_DIR)
    files = Dir.glob(File.join(tmp_dir, '*.html')).sort
  end

  # Parsing
  agent = Mechanize.new
  agent.pluggable_parser.html = MtgDb::Parsers::GathererParser

  files.each do |file|
    filepath = File.absolute_path file
    uri = "file://#{filepath}"

    page = agent.get(uri) # uses our pluggable parser

    # insert each card into the db, creating records in associated tables if necessary
    page.cards.each do |card|
      puts "\tProcessing Card: #{card[:name]}"

      card_model = MtgDb::Models::Card.new
      card_model.set_fields(card, %i[name mana_cost cmc supertype subtype rules power toughness])
      card_model.save

      # Set/Rarity a.k.a Set Version
      card[:set_versions].each do |set_version|
        set = MtgDb::Models::CardSet.find_or_create(
          name: set_version[:set],
          abbreviation: set_version[:set_abbreviation]
        )
        rarity = MtgDb::Models::Rarity.find_or_create(
          name: set_version[:rarity],
          abbreviation: set_version[:rarity][0]
        )
        MtgDb::Models::SetVersion.create(
          card: card_model,
          multiverse_id: set_version[:multiverse_id],
          card_set: set,
          rarity: rarity
        )
      end

      # Planeswalker
      if card_model.is_planeswalker?
        MtgDb::Models::Planeswalker.create(card: card_model, loyalty: card[:loyalty])
      end

      # Vanguard
      if card_model.is_vanguard?
        MtgDb::Models::Vanguard.create(
          card: card_model,
          hand_modifier: card[:hand_modifier],
          life_modifier: card[:life_modifier]
        )
      end
    end
  end

  db.disconnect
end
add_double_faced_cards_to_db(db_filename, tmp_dir) click to toggle source

Adding Downloaded Double-Faced Cards to Db

# File lib/mtg_db.rb, line 133
def self.add_double_faced_cards_to_db(db_filename, tmp_dir)
  tmp_dir ||= TMP_DIR
  tmp_dir = File.join(tmp_dir, DOUBLE_FACED_DIR)
  files = Dir.glob(File.join(tmp_dir, '*.html')).sort

  agent = Mechanize.new
  agent.pluggable_parser.html = MtgDb::Parsers::DoubleFacedCardDetailsParser

  # Connect to db
  db = Sequel.sqlite(db_filename)
  db.run 'PRAGMA synchronous = 0'
  # db.loggers << Logger.new($stdout)
  require 'mtg_db/models'

  files.each do |file|
    filepath = File.absolute_path file
    uri = "file://#{filepath}"
    puts uri

    page = agent.get(uri) # uses our pluggable parser

    # insert each card into the db, creating records in associated tables if necessary
    if page.cards.size == 2
      faceup_card = MtgDb::Models::Card.where(name: page.faceup_card_name).first
      facedown_card = MtgDb::Models::Card.where(name: page.facedown_card_name).first

      puts "\tProcessing Double-Faced Card: #{faceup_card.name} <=> #{facedown_card.name}"
      model = MtgDb::Models::DoubleFaced.find_or_create(faceup_card: faceup_card, facedown_card: facedown_card)
    end
  end

  db.disconnect
end
create_db(name) click to toggle source
# File lib/mtg_db.rb, line 22
def self.create_db(name)
  schema = File.new(SCHEMA_FILENAME).readlines.join

  db = Sequel.sqlite(name)
  db.run(schema)
  return db
end
download_all_cards(tmp_dir) click to toggle source

Downloading NOTE: The download directory must be empty for downloads to start

# File lib/mtg_db.rb, line 32
def self.download_all_cards(tmp_dir)
  tmp_dir ||= TMP_DIR
  tmp_dir = File.join(tmp_dir, ALL_CARDS_DIR)

  downloader = MtgDb::Downloaders::AllCardsStandardDownloader.new(output_dir: tmp_dir)
  downloader.start if downloader.is_empty?
  @standard_files_downloaded = downloader.files
end
download_double_faced_cards(db_filename, tmp_dir) click to toggle source

Downloading Double-Faced Cards

# File lib/mtg_db.rb, line 111
def self.download_double_faced_cards(db_filename, tmp_dir)
  tmp_dir ||= TMP_DIR
  tmp_dir = File.join(tmp_dir, DOUBLE_FACED_DIR)

  downloader = MtgDb::Downloaders::CardDetailsDownloader.new(output_dir: tmp_dir)

  # Connect to db
  db = Sequel.sqlite(db_filename)
  # db.loggers << Logger.new($stdout)
  require 'mtg_db/models'

  cards = MtgDb::Models::Card.where(Sequel.ilike(:rules, '%transform%')).all
  cards.each do |card|
    multiverse_id = card.set_versions.first.multiverse_id
    puts "#{card.name}, #{multiverse_id}"
    downloader.start(card.name.parameterize, multiverse_id)
  end

  db.disconnect
end
is_sqlite3?(db_filename) click to toggle source

Use this to test if a given filename is an sqlite3 db

# File lib/mtg_db.rb, line 168
def self.is_sqlite3?(db_filename)
  header = IO.binread(db_filename, SQLITE3_HEADER_STRING_LENGTH)
  return (header == SQLITE3_HEADER_STRING)
end
mangle(db_filename) click to toggle source

Mangle the header of an SQLite3 file

# File lib/mtg_db.rb, line 174
def self.mangle(db_filename)
  header = ''
  SQLITE3_HEADER_STRING_LENGTH.times do
    header += (Random.rand(26) + 48).chr
  end

  File.open(db_filename, 'r+b') do |file|
    file.seek(0, IO::SEEK_SET)
    file.print(header)
  end
end