lib/pokepaste.rb in pokepaste-0.0 vs lib/pokepaste.rb in pokepaste-0.1

- old
+ new

@@ -1,10 +1,112 @@ -require "pokepaste/paste" require "pokepaste/pokemon" +require "pokepaste/team" +require "open-uri" + module PokePaste - def parse(paste) + def self.parse(paste) + # Each paragraph is a Pokémon (separated by 1 or more blank lines) + paragraphs = paste.strip.split(/\n\s*\n/).map { |p| p.split("\n").map &:strip } + + team = PokePaste::Team.new + + paragraphs.each do |paragraph| + # Line 1 can contain species, nickname, gender, and item in format like: + # Bud (Bulbasaur) (M) @ Life Orb + # Only the species is required, however + line1 = paragraph.shift + before_at, item = line1.split /\s+@\s+/ + + # Now work from right to left, parsing off the parts in parens + + # Set the gender to nil, :male, or :female + if gender = before_at.slice!(/\s*\((m(ale)?|f(emale)?)\)$/i) + # Get rid of the parens + [/^\s*\(/, /\)$/].each { |regex| gender.slice! regex } + gender = PokePaste::Pokemon.gender_abbrs[gender.downcase[0].to_sym] + end + + # This regex requires a space between the nickname and opening paren + if species = before_at.slice!(/\s+\([^\)]+\)$/i) + # We found the species inside parens, which means a nickname is present + [/^\s*\(/, /\)$/].each { |regex| species.slice! regex } + nickname = before_at + else + # Otherwise all that remains is the species, and there is no nickname + species = before_at + nickname = nil + end + + pkmn = PokePaste::Pokemon.new species: species, nickname: nickname, + item: item, gender: gender + + # Pull the moves out + pkmn.moves, paragraph = paragraph.partition { |line| line =~ /^\s*-/ } + if pkmn.moves.any? + pkmn.moves.map! do |line| + move_slot = line.sub /^\s*-\s*/, "" + move_slot.include?("/") ? move_slot.split(/\s*\/\s*/) : move_slot + end + end + + # Pull the nature out. If there are multiple, the first is used + nature_line, paragraph = paragraph.partition { |line| line =~ /nature\s*$/i } + if nature_line.any? + pkmn.nature = nature_line.first.sub(/\s*nature\s*$/i, "").downcase.to_sym + end + + # All that we're left with at this point are attributes that are denoted with + # the syntax "Attribute: Value". Let's loop over them and parse the attribute + paragraph.each do |line| + attribute, value = line.split /\s*:\s*/, 2 + attribute = attribute.downcase.gsub(/\s+/, "_").to_sym + + # At this point attribute will be a symbol with the snake_case approximation + # of whatever was before the first ":" in the string. If we know what it does, + # we can manipulate it as desired, otherwise just let it through as a string. + # This is intended to be a flexible format. + if %i[evs ivs].include?(attribute) + data = value.split /\s*\/\s*/ + data.map { |d| d.split /\s+/, 2 }.each do |value, stat| + current_value = pkmn.send attribute + current_value[stat.downcase.to_sym] = value.to_i + pkmn.send "#{attribute}=", current_value + end + elsif %i[level happiness].include?(attribute) + pkmn.send "#{attribute}=", value.to_i + elsif attribute == :shiny + pkmn.shiny = %w[yes true].include?(value.downcase) + else + pkmn.send "#{attribute}=", value + end + end + + team << pkmn + end + + team end - def fetch(str) + def self.fetch(str) + input_url = URI.parse(str) + unless [nil, "pokepast.es"].include? input_url.host + raise ArgumentError, "invalid URL; must be valid pokepast.es URL or path, received '#{str}'" + end + + path = (input_url.host ? input_url.path : str).gsub /^\//, "" + if path.empty? + raise ArgumentError, "invalid URL; no paste ID found in '#{str}'" + end + + # The paste is in <article> tags + paste_content = URI.open("https://pokepast.es/#{path}").read. + slice(/(?<=\<article\>)[\S\s]*(?=\<\/article\>)/) + + unless paste_content + raise OpenURI::HTTPError.new "no paste found at '#{str}'", 404 + end + + # Removing all HTML tags is sufficient to pass the string to the parser + parse paste_content.gsub(/\<\/?[^\>]+\>/, "") end end