#-- # Code copyright Lisa Seelye, 2007. www.crudvision.com # Reve is not licensed for commercial use. For other uses there are no # restrictions. # # The author is not adverse to tokens of appreciation in the form of Eve ISK, # ships, and feedback. Please use # http://www.crudvision.com/reve-ruby-eve-online-api-library/ to provide # feedback or send ISK to Raquel Smith in Eve. :-) #++ begin require 'hpricot' rescue LoadError require 'rubygems' require 'hpricot' end require 'net/http' $:.unshift(File.dirname(__FILE__)) unless $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__))) require 'lib/exceptions' require 'lib/extensions' require 'lib/classes' module Reve # API Class. # Basic Usage: # api = Reve::API.new('my_UserID', 'my_apiKey') # alliances = api.alliances # Returns an array of Reve::Classes::Alliance # # api.personal_wallet_blanace(:characterid => 892008733) # Returns an array of # Reve::Classes::WalletBalance. Note that the CharacterID Number is required # here. # # api.sovereignty :just_hash => true # Returns the hash for this call with no # Alliance data with it. # # As of Revision 22 (28 August 2007) all API calls take a parameter, # :just_hash, to just get the hash that represents that particular API call; # No data related to the call is returned if :just_hash is present class API @@alliances_url = 'http://api.eve-online.com/eve/AllianceList.xml.aspx' @@sovereignty_url = 'http://api.eve-online.com/map/Sovereignty.xml.aspx' @@reftypes_url = 'http://api.eve-online.com/eve/RefTypes.xml.aspx' @@skill_tree_url = 'http://api.eve-online.com/eve/SkillTree.xml.aspx' @@member_tracking_url = 'http://api.eve-online.com/corp/MemberTracking.xml.aspx' @@corporate_wallet_balance_url = 'http://api.eve-online.com/corp/AccountBalance.xml.aspx' @@personal_wallet_balance_url = 'http://api.eve-online.com/char/AccountBalance.xml.aspx' @@corporate_wallet_trans_url = 'http://api.eve-online.com/corp/WalletTransactions.xml.aspx' @@personal_wallet_trans_url = 'http://api.eve-online.com/char/WalletTransactions.xml.aspx' @@corporate_wallet_journal_url = 'http://api.eve-online.com/corp/WalletJournal.xml.aspx' @@personal_wallet_journal_url = 'http://api.eve-online.com/char/WalletJournal.xml.aspx' @@characters_url = 'http://api.eve-online.com/account/Characters.xml.aspx' @@training_skill_url = 'http://api.eve-online.com/char/SkillInTraining.xml.aspx' @@character_sheet_url = 'http://api.eve-online.com/char/CharacterSheet.xml.aspx' @@starbases_url = 'http://api.eve-online.com/corp/StarbaseList.xml.aspx' @@starbasedetail_url = 'http://api.eve-online.com/corp/StarbaseList.xml.aspx' @@conqurable_outposts_url = 'http://api.eve-online.com/eve/ConquerableStationList.xml.aspx' @@corporation_sheet_url = 'http://api.eve-online.com/corp/CorporationSheet.xml.aspx' @@errors_url = 'http://api.eve-online.com/eve/ErrorList.xml.aspx' @@map_jumps_url = 'http://api.eve-online.com/map/Jumps.xml.aspx' @@map_kills_url = 'http://api.eve-online.com/map/Kills.xml.aspx' @@personal_market_orders_url = 'http://api.eve-online.com/char/MarketOrders.xml.aspx' @@corporate_market_orders_url = 'http://api.eve-online.com/corp/MarketOrders.xml.aspx' @@personal_industry_jobs_url = 'http://api.eve-online.com/char/IndustryJobs.xml.aspx' @@corporate_industry_jobs_url = 'http://api.eve-online.com/corp/IndustryJobs.xml.aspx' @@personal_assets_url = 'http://api.eve-online.com/char/AssetList.xml.aspx' @@corporate_assets_url = 'http://api.eve-online.com/corp/AssetList.xml.aspx' @@personal_kills_url = 'http://api.eve-online.com/char/KillLog.xml.aspx' @@corporate_kills_url = 'http://api.eve-online.com/corp/KillLog.xml.aspx' cattr_accessor :character_sheet_url, :training_skill_url, :characters_url, :personal_wallet_journal_url, :corporate_wallet_journal_url, :personal_wallet_trans_url, :corporate_wallet_trans_url, :personal_wallet_balance_url, :corporate_wallet_balance_url, :member_tracking_url, :skill_tree_url, :reftypes_url, :sovereignty_url, :alliances_url, :starbases_url, :starbasedetail_url, :conqurable_outposts_url, :corporation_sheet_url, :map_jumps_url, :map_kills_url, :personal_market_orders_url, :corporate_market_orders_url, :personal_industry_jobs_url, :corporate_industry_jobs_url, :personal_assets_url, :corporate_assets_url, :personal_kills_url, :corporate_kills_url attr_accessor :key, :userid, :charid attr_reader :current_time, :cached_until, :last_hash # Create a new API instance. # current_time and cached_until are meaningful only for the LAST call made. # Expects: # * userid ( Integer | String ) - Your API userID # * key ( String ) - Your API key (Full key or restricted key) # * charid ( Integer | String ) - Default characterID for calls requiring it. # NOTE: All values passed to the constructor are typecasted to a String for safety. def initialize(userid = "", key = "", charid = "") @userid = (userid || "").to_s @key = (key || "").to_s @charid = (charid || "").to_s @max_tries = 3 @current_time = nil @cached_until = nil @last_hash = nil end def userid=(u) #:nodoc: @userid = u.to_s end def key=(k) #:nodoc: @key = k.to_s end def charid=(c) #:nodoc: @charid = c.to_s end # Return a list of Alliances from # http://api.eve-online.com/eve/AllianceList.xml.aspx # See also: Reve::Classes::Alliance def alliances(opts = {}) compute_hash( opts.merge(:url => @@alliances_url) ) || process_query(Reve::Classes::Alliance,@@alliances_url,false) end def map_jumps(opts = {}) compute_hash( opts.merge(:url => @@map_jumps_url) ) || process_query(Reve::Classes::MapJump,@@map_jumps_url,false) end def map_kills(opts = {}) compute_hash( opts.merge(:url => @@map_kills_url) ) || process_query(Reve::Classes::MapKill,@@map_kills_url,false) end # Returns a list of API Errors def errors(opts = {}) compute_hash( opts.merge(:url => @@errors_url) ) || process_query(Reve::Classes::APIError,@@errors_url,false) end # Returns the Sovereignty list from # http://api.eve-online.com/map/Sovereignty.xml.aspx # See also: Reve::Classes::Sovereignty def sovereignty(opts = {}) compute_hash( opts.merge(:url => @@sovereignty_url) ) || process_query(Reve::Classes::Sovereignty,@@sovereignty_url,false) end # Returns a RefType list (whatever they are) from # http://api.eve-online.com/eve/RefTypes.xml.aspx # See also: Reve::Classes::RefType def ref_types(opts = {}) compute_hash( opts.merge(:url => @@reftypes_url) ) || process_query(Reve::Classes::RefType,@@reftypes_url,false) end # Returns a list of ConqurableStations and outposts from # http://api.eve-online.com/eve/ConquerableStationList.xml.aspx # See also: Reve::Classes::ConqurableStation def conqurable_stations(opts = {}) compute_hash( opts.merge(:url => @@conqurable_outposts_url) ) || process_query(Reve::Classes::ConqurableStation, @@conqurable_outposts_url, false) end # Returns a list of Reve::Classes::MarketOrder objects for market orders that are up # Pass the characterid of the Character to check for def personal_market_orders(opts = {:characterid => nil}) args = postfields(opts) h = compute_hash(args.merge(:url => @@personal_market_orders_url)) return h if h process_query(Reve::Classes::PersonalMarketOrder, @@personal_market_orders_url, false, args) end # Returns a list of Reve::Classes::MarketOrder objects for market orders that are up on behalf of a Corporation # Pass the characterid of the Character of whose corporation to check for def corporate_market_orders(opts = {:characterid => nil}) args = postfields(opts) h = compute_hash(args.merge(:url => @@corporate_market_orders_url)) return h if h process_query(Reve::Classes::PersonalMarketOrder, @@corporate_market_orders_url, false, args) end # Returns a list of Reve::Classes::PersonalIndustryJob objects. def personal_industry_jobs(opts = {:characterid => nil}) args = postfields(opts) h = compute_hash(args.merge(:url => @@personal_industry_jobs_url)) return h if h process_query(Reve::Classes::PersonalIndustryJob, @@personal_industry_jobs_url,false,args) end # Returns a list of Reve::Classes::CorporateIndustryJob objects. def corporate_industry_jobs(opts = {:characterid => nil}) args = postfields(opts) h = compute_hash(args.merge(:url => @@corporate_industry_jobs_url)) return h if h process_query(Reve::Classes::CorporateIndustryJob, @@corporate_industry_jobs_url,false,args) end # Returns the SkillTree from # http://api.eve-online.com/eve/SkillTree.xml.aspx # See also: Reve::Classes::SkillTree # NOTE: This doesn't actually return a 'tree' yet. def skill_tree(opts = {}) h = compute_hash(opts.merge(:url => @@skill_tree_url) ) return h if h doc = process_query(nil,@@skill_tree_url,true) skills = [] (doc/'rowset[@name=skills]/row').each do |skill| name = skill['typename'] type_id = skill['typeid'] group_id = skill['groupid'] rank = (skill/:rank).inner_html desc = (skill/:description).inner_html required_skills = [] reqs = (skill/'rowset@name=[requiredskills]/row') reqs.each do |required| next if required.kind_of? Hpricot::Text # why is this needed? Why is this returned? How can I only get stuff with typeid and skilllevel? required_skills << Reve::Classes::SkillRequirement.new(required) if required['typeid'] && required['skilllevel'] end required_attribs = [] (skill/'requiredattributes').each do |req| pri = doc.at(req.xpath + "/primaryattribute") sec = doc.at(req.xpath + "/secondaryattribute") required_attribs << Reve::Classes::PrimaryAttribute.new(pri.inner_html) required_attribs << Reve::Classes::SecondaryAttribute.new(sec.inner_html) end bonuses = [] res = (skill/'rowset@name=[skillbonuscollection]/row') res.each do |bonus| next if bonus.kind_of? Hpricot::Text bonuses << Reve::Classes::SkillBonus.new(bonus) if bonus['bonustype'] && bonus['bonusvalue'] end skills << Reve::Classes::SkillTree.new(name,type_id,group_id,desc,rank,required_attribs,required_skills,bonuses) end skills end # Does big brother tracking from # http://api.eve-online.com/corp/MemberTracking.xml.aspx # Expects: # * charid ( Integer | String ) - Look at players in this Character's Corporation # See also: Reve::Classes::MemberTracking def member_tracking(opts = {:characterid => nil}) args = postfields(opts) h = compute_hash(args.merge(:url => @@member_tracking_url)) return h if h process_query(Reve::Classes::MemberTracking,@@member_tracking_url,false,args) end # Gets one's own personal WalletBalance from # http://api.eve-online.com/char/AccountBalance.xml.aspx # Expects: # * charid ( Integer | String ) - Look at this player's WalletBalance # See also: Reve::Classes::WalletBalance and corporate_wallet_balance def personal_wallet_balance(opts = { :characterid => nil }) args = postfields(opts) h = compute_hash(args.merge(:url => @@personal_wallet_balance_url)) return h if h process_query(Reve::Classes::WalletBalance,@@personal_wallet_balance_url,false,args) end # Gets one's corporate WalletBalance from # http://api.eve-online.com/corp/AccountBalance.xml.aspx # Expects: # * charid ( Integer | String ) - Look at WalletBalance objects from this Character's Corporation # See also: Reve::Classes::WalletBalance and personal_wallet_balance def corporate_wallet_balance(opts = { :characterd => nil }) args = postfields(opts) h = compute_hash(args.merge(:url => @@corporate_wallet_balance_url)) return h if h process_query(Reve::Classes::WalletBalance,@@corporate_wallet_balance_url,false,args) end # Gets one's own personal WalletTransaction list from # http://api.eve-online.com/char/WalletTransactions.xml.aspx # Expects: # * characterid ( Integer | String ) - Look at this player's WalletTransaction list # * before_trans_id ( Integer | String ) - Gets a list of WalletTransaction objects from before this Transaction ID. # See also: Reve::Classes::WalletTransaction and # corporate_wallet_transactions def personal_wallet_transactions(opts = { :characterid => nil, :before_trans_id => nil }) args = postfields(opts) h = compute_hash(args.merge(:url => @@personal_wallet_trans_url) ) return h if h process_query(Reve::Classes::PersonalWalletTransaction,@@personal_wallet_trans_url,false,opts) end # Gets one's corporate WalletTransaction list from # http://api.eve-online.com/corp/WalletTransactions.xml.aspx # Expects: # * account_key ( Integer | String ) - Account key (1000-1006) to look at. # * charid ( Integer | String ) - Look at WalletTransaction objects from this Character's Corporation # * before_trans_id ( Integer | String ) - Gets a list of WalletTransaction objects from before this Transaction ID. # See also: Reve::Classes::WalletTransaction and # personal_wallet_transactions def corporate_wallet_transactions(opts = {:accountkey => nil, :characterid => nil, :beforerefid => nil}) args = postfields(opts) h = compute_hash(args.merge(:url => @@corporate_wallet_trans_url)) return h if h process_query(Reve::Classes::CorporateWalletTransaction,@@corporate_wallet_trans_url,false,args) end # Gets one's own corporate WalletJournal list from # http://api.eve-online.com/corp/WalletJournal.xml.aspx # Expects: # * account_key ( Integer | String ) - Account key (1000-1006) to look at. # * charid ( Integer | String ) - Look at WalletJournal objects from this Character's Corporation # * before_ref_id ( Integer | String ) - Gets a list of WalletTransaction objects from before this RefID. # See also: Reve::Classes::WalletJournal and personal_wallet_journal def corporate_wallet_journal(opts = {:accountkey => nil, :characterid => nil, :beforerefid => nil}) args = postfields(opts) h = compute_hash(args.merge(:url => @@corporate_wallet_journal_url)) return h if h process_query(Reve::Classes::WalletJournal,@@corporate_wallet_journal_url,false,args) end # Gets one's own personal WalletJournal list from # http://api.eve-online.com/char/WalletJournal.xml.aspx # Expects: # * charid ( Integer | String ) - Look at this player's WalletJournal list # * before_ref_id ( Integer | String ) - Gets a list of WalletJournal objects from before this RefID. # See also: Reve::Classes::WalletJournal and corporate_wallet_journal def personal_wallet_journal(opts = { :characterid => nil, :beforerefid => nil} ) args = postfields(opts) h = compute_hash(args.merge(:url => @@personal_wallet_journal_url)) return h if h process_query(Reve::Classes::WalletJournal,@@personal_wallet_journal_url,false,args) end # Get a list of personal assets for the characterid. def personal_assets_list(opts = { :characterid => nil }) args = postfields(opts) h = compute_hash(args.merge(:url => @@personal_assets_url)) return h if h xml = process_query(nil,@@personal_assets_url,true,args) assets = [] xml.search("/eveapi/result/rowset[@name='assets']/row").each do |container| asset_container = Reve::Classes::AssetContainer.new(container) container.search("rowset[@name='contents']/row").each do |asset| asset_container.assets << Reve::Classes::Asset.new(asset) end assets << asset_container end assets end # Get a list of the Corporate Assets. Pass the characterid of the Corporate member See also assets_list method def corporate_assets_list(opts = { :characterid => nil}) args = postfields(opts) h = compute_hash(args.merge(:url => @@corporate_assets_url)) return h if h xml = process_query(nil,@@corporate_assets_url,true,args) assets = [] xml.search("/eveapi/result/rowset/row").each do |container| asset_container = Reve::Classes::AssetContainer.new(container) container.search("rowset[@name='contents']/row").each do |asset| asset_container.assets << Reve::Classes::Asset.new(asset) end assets << asset_container end assets end # Returns a Character list for the associated key and userid from # http://api.eve-online.com/account/Characters.xml.aspx # See also: Reve::Classes::Character def characters(opts = {}) args = postfields(opts) h = compute_hash(args.merge(:url => @@characters_url)) return h if h process_query(Reve::Classes::Character,@@characters_url,false,args) end # Gets the SkillInTraining from # http://api.eve-online.com/char/SkillInTraining.xml.aspx # Expects: # * charid ( Integer | String ) - Get the SkillInTraining for this Character # See also: Reve::Classes::SkillInTraining def skill_in_training(opts = {:characterid => nil}) args = postfields(opts) ch = compute_hash(args.merge(:url => @@training_skill_url)) return ch if ch h = {} xml = process_query(nil,@@training_skill_url,true,args) xml.search("//result").each do |elem| for field in [ 'currentTQTime', 'trainingEndTime','trainingStartTime','trainingTypeID','trainingStartSP','trainingDestinationSP','trainingToLevel','skillInTraining' ] field.downcase! h[field] = (elem/field.intern).inner_html end end Reve::Classes::SkillInTraining.new(h) end # Returns a list of Reve::Classes::Starbase for charid's Corporation. # http://api.eve-online.com/corp/StarbaseList.xml.aspx # Expects: # * charid ( Integer | String ) - Get the Starbase list for this character's Corporation # See also Reve::Classes::Starbase def starbases(opts = { :characterid => nil}) args = postfields(opts) h = compute_hash(args.merge(:url => @@starbases_url)) return h if h process_query(Reve::Classes::Starbase,@@starbases_url,false,args) end # Returns the fuel status for the Starbase whose item id is starbase_id # http://api.eve-online.com/corp/StarbaseDetail.xml.aspx # Expects: # * charid ( Integer | String ) - Get the Starbase associated wih this character's Corporation # * starbase_id ( Integer ) - Get the fuel for this Starbase. This is the Starbase's itemid. # See also Reve::Classes::StarbaseFuel def starbase_fuel(opts = { :characterid => nil, :starbaseid => nil }) args = postfields(opts) h = compute_hash(args.merge(:url => @@starbasedetail_url)) return h if h ret = process_query(Reve::Classes::StarbaseFuel,@@starbasedetail_url, false, args) ret.each { |r| r.starbase_id = opts[:starbaseid] } ret end def personal_kills(opts = { :characterid => nil }) args = postfields(opts) h = compute_hash(args.merge(:url => @@personal_kills_url)) return h if h xml = process_query(nil,@@personal_kills_url,true,args) kills = [] xml.search("/eveapi/result/rowset/row").each do |e| victim = Reve::Classes::KillVictim.new(e.search("victim").first) rescue next # cant find victim attackers = [] losses = [] e.search("rowset[@name='attackers']/row").each do |attacker| attackers << Reve::Classes::KillAttacker.new(attacker) end e.search("rowset[@name='items']/row").each do |lost_item| lost = Reve::Classes::KillLoss.new(lost_item) lost_item.search("rowset[@name='items']/row").each do |contained| lost.contained_losses << Reve::Classes::KillLoss.new(contained) end losses << lost end kills << Reve::Classes::Kill.new(victim, attackers, losses) end kills end # Gets the CorporationSheet from http://api.eve-online.com/corp/CorporationSheet.xml.aspx # Expects: # * Hash of arguments: # * * characterid ( Integer | String ) - Gets the CorporationSheet for this Character # See also: Reve::Classes::CorporationSheet def corporation_sheet(opts = { :characterid => nil }) args = postfields(opts) h = compute_hash(args.merge(:url => @@corporation_sheet_url)) return h if h xml = process_query(nil,@@corporation_sheet_url,true,args) h = { 'graphicid' => 0, 'shape1' => 0, 'shape2' => 0, 'shape3' => 0, 'color1' => 0, 'color2' => 0, 'color3' => 0, } h.keys.each { |k| h[k] = xml.search("//result/logo/" + k + "/").to_s.to_i } corporate_logo = Reve::Classes::CorporateLogo.new h wallet_divisions = xml.search("//result/rowset[@name='walletDivisions']/").collect { |k| k if k.kind_of? Hpricot::Elem } - [ nil ] divisions = xml.search("//result/rowset[@name='divisions']/").collect { |k| k if k.kind_of? Hpricot::Elem } - [ nil ] divisions.collect! { |d| Reve::Classes::CorporateDivision.new(d) } wallet_divisions.collect! { |w| Reve::Classes::WalletDivision.new(w) } # Map the XML names to our own names and assign them to the temporary # hash +res+ to pass to Reve::Classes::CorporationSheet#new res = Hash.new { :corporationid => :id, :corporationname => :name, :ticker => :ticker, :ceoid => :ceo_id, :ceoname => :ceo_name, :stationid => :station_id, :stationname => :station_name, :description => :description, :url => :url, :allianceid => :alliance_id, :alliancename => :alliance_name, :taxrate => :tax_rate, :membercount => :member_count, :memberlimit => :member_limit }.each do |k,v| res[v] = xml.search("//result/#{k.to_s}/").first.to_s.strip end Reve::Classes::CorporationSheet.new res, divisions, wallet_divisions, corporate_logo end # Gets the CharacterSheet from # http://api.eve-online.com/char/CharacterSheet.xml.aspx # Expects: # * charid ( Integer | String ) - Get the CharacterSheet for this Character # See also: Reve::Classes::CharacterSheet def character_sheet(opts = { :characterid => nil }) args = postfields(opts) h = compute_hash(args.merge(:url => @@character_sheet_url)) return h if h xml = process_query(nil,@@character_sheet_url,true,args) cs = Reve::Classes::CharacterSheet.new (xml/:result/:attributeenhancers).each do |enh| for kind in ['intelligenceBonus', 'memoryBonus', 'charismaBonus', 'perceptionBonus','willpowerBonus'] thing = nil case kind when 'intelligenceBonus' thing = Reve::Classes::IntelligenceEnhancer when 'memoryBonus' thing = Reve::Classes::MemoryEnhancer when 'charismaBonus' thing = Reve::Classes::CharismaEnhancer when 'perceptionBonus' thing = Reve::Classes::PerceptionEnhancer when 'willpowerBonus' thing = Reve::Classes::WillpowerEnhancer else thing = Reve::Classes::AttributeEnhancer end (enh/kind.downcase.intern).each do |b| name = (b/:augmentatorname).inner_html value = (b/:augmentatorvalue).inner_html cs.enhancers << thing.new(name,value) end end end (xml/:result).each do |elem| for field in [ 'characterid', 'name', 'race', 'bloodline', 'gender','corporationname','corporationid','balance' ] cs.send("#{field}=",(elem/field.intern).first.inner_html) end end (xml/:result/:attributes).each do |elem| for attrib in [ 'intelligence','memory','charisma','perception','willpower' ] cs.send("#{attrib}=",(elem/attrib.intern).first.inner_html) end end (xml/:result/:rowset/:row).each do |elem| cs.skills << Reve::Classes::Skill.new(elem) end cs end protected # Sets up the post fields for Net::HTTP::Post hash for process_query method. # TODO: Consider moving this whole thing into process_query to avoid # calling this in every method! def postfields(opts = {}) ret = { 'userID' => @userid,'apiKey' => @key }.merge(opts) ret.inject({}) do |n, (k,v)| n[k] = v.to_s if v n end end # Creates a hash for some hash of postfields. For each API method pass # :just_hash => to something to return a hash that can be matched to # the last_hash instance method created in process_query. # This method is called in each API method before process_query and if # :just_hash was passed in args then a String will be returned, otherwise # nil will be returned # TODO: Consider moving this whole thing into process_query before the URI parsing def compute_hash(args = {}) return nil unless args.include?(:just_hash) args.delete(:just_hash) url = args[:url] args.delete(:url) spl = url.split '/' ret = (spl[-2] + '/' + spl[-1]) + ':' args.delete_if { |k,v| (v || "").to_s.length == 0 } # Delete keys if the value is nil h = args.stringify_keys ret += h.sort.flatten.collect{ |e| e.to_s }.join(':') ret.gsub(/:$/,'') end # Processes a URL and for simple results # create an array of objects of type klass. Or just return the XML if # just_xml is set true. args is from postfields # This method will call check_exception to see if an Exception came from # CCP. # Expects: # * klass ( Class ) - The class container for parsing. An array of these is returned in default behaviour. # * url ( String ) - API URL # * just_xml ( Boolean ) - Return only the XML and not attempt to parse //rowset/row. Useful if the XML is not in that form. # * args ( Hash ) - Hash of arguments for the request. See postfields method. def process_query(klass, url, just_xml = false, opts = {}) #args = postfields(opts) #h = compute_hash(args.merge(:url => url)) #return h if h uri = URI.parse(url) req = Net::HTTP::Post.new(uri.path) req.set_form_data(opts.merge({'version' => 2})) res = Net::HTTP.new(uri.host, uri.port).start {|http| http.request(req) } response = nil 0.upto(@max_tries) do begin case res when Net::HTTPSuccess, Net::HTTPRedirection response = res.body end rescue Exception next end break if response end raise Reve::Exceptions::ReveNetworkStatusException.new(res.body) unless response @last_hash = compute_hash(opts.merge({:url => url, :just_hash => true })) # compute hash xml = check_exception(response) return xml if just_xml return [] if xml.nil? # No XML document returned. We should panic. ret = [] xml.search("//rowset/row").each do |elem| ret << klass.new(elem) end ret end # Raises the proper exception (if there is one), otherwise it returns the # XML response. def check_exception(xml) x = Hpricot(xml) begin out = x.search("//error") # If this fails then there are some big problems with Hpricot#search ? rescue Exception => e $stderr.puts "Fatal error ((#{e.to_s})): Couldn't search the XML document ((#{xml})) for any potential error messages! Is your Hpricot broken?" exit 1 end @current_time = (x/:currenttime).inner_html.to_time rescue Time.now.utc # Shouldn't need to rescue this but one never knows @cached_until = (x/:cacheduntil).inner_html.to_time rescue nil # Errors aren't always cached return x if out.size < 1 code = out.first['code'].to_i str = out.first.inner_html Reve::Exceptions.raise_it(code,str) end end end