Skip to content
Browse files

Merge pull request #11 from fmorales/master

Rollup of Oct - Dec 2013
  • Loading branch information...
2 parents a30d232 + c6859e5 commit b0e07e457e4a0948d5c85f9c0dc49f97cf6ebc73 @fmorales fmorales committed Jan 22, 2014
View
16 .travis.yml
@@ -0,0 +1,16 @@
+language: ruby
+rvm:
+ - "1.9.2"
+before_script:
+ - echo "Remember your flashlight! Watch out for the lurking Grue!"
+script:
+ - echo "BEGIN script BLOCK"
+ - bundle exec ruby test/test_reve.rb
+ #- bundle exec ruby test/test_extensions.rb
+
+
+
+
+
+
+
View
6 Gemfile
@@ -1,6 +1,12 @@
source 'https://rubygems.org'
+group :deployment do
+ gem 'hpricot'
+ gem 'simplecov', :require => false #code coverage thingy
+end
+
group :test, :development do
gem 'hpricot'
gem 'simplecov', :require => false #code coverage thingy
end
+
View
18 Gemfile.lock
@@ -0,0 +1,18 @@
+GEM
+ remote: https://rubygems.org/
+ specs:
+ docile (1.1.1)
+ hpricot (0.8.6)
+ multi_json (1.8.2)
+ simplecov (0.8.2)
+ docile (~> 1.1.0)
+ multi_json
+ simplecov-html (~> 0.8.0)
+ simplecov-html (0.8.0)
+
+PLATFORMS
+ ruby
+
+DEPENDENCIES
+ hpricot
+ simplecov
View
213 lib/reve.rb
@@ -23,6 +23,7 @@
require 'reve/exceptions'
require 'reve/extensions'
require 'reve/classes'
+require 'reve/processing_helpers'
module Reve
@@ -51,6 +52,7 @@ class API
@@account_status_url = BASE_URL + '/account/AccountStatus.xml.aspx'
@@research_url = BASE_URL + '/char/Research.xml.aspx'
+ @@contracts_url = BASE_URL + '/char/Contracts.xml.aspx'
@@personal_notification_url = BASE_URL + '/char/Notifications.xml.aspx'
@@personal_mailing_lists_url = BASE_URL + '/char/mailinglists.xml.aspx'
@@personal_mail_messages_url = BASE_URL + '/char/MailMessages.xml.aspx'
@@ -412,6 +414,20 @@ def research(opts = { :characterid => nil })
process_query(Reve::Classes::Research,opts[:url] || @@research_url,false,args)
end
+ #
+ #gets contracts
+ # http://api.eve-online/char/Contracts.xml.aspx
+ # * characterid ( Integer | String ) - Get stats for this Character
+ # See also: Reve::Classes::Contract
+
+ def contracts(opts = { :characterid => nil })
+ args = postfields(opts)
+ h = compute_hash(args.merge(:url => @@contracts_url))
+ return h if h
+ process_query(Reve::Classes::Contracts,opts[:url] || @@contracts_url,false,args)
+ end
+
+
# Gets one's own personal WalletBalance from
# http://api.eve-online.com/char/AccountBalance.xml.aspx
# Expects:
@@ -1048,200 +1064,7 @@ def character_info(opts = { :characterid => nil })
Reve::Classes::CharacterInfo.new(xml.search('//result').first)
end
- protected
- # Helper method to handle nested assets
- def recur_through_assets(rows)
- assets = []
- rows.each do |container|
- unless container.empty?
- asset_container = Reve::Classes::AssetContainer.new(container)
- asset_container.assets = self.recur_through_assets(container.search("/rowset/row"))
- assets << asset_container
- else
- assets << Reve::Classes::Asset.new(container)
- end
- end
- assets
- end
-
- # Sets up the post fields for Net::HTTP::Get hash for process_query method.
- # See also format_url_request
- # TODO: Consider moving this whole thing into process_query to avoid
- # calling this in every method!
- def postfields(opts = {})
- baseargs = { :characterid => @charid }
- if @cak
- baseargs[:keyid] = @keyid
- baseargs[:vcode] = @key
- else
- baseargs[:userid] = @keyid
- baseargs[:apikey] = @key
- end
- ret = opts.clone
- baseargs.each do |k,v|
- if ret[k].nil?
- ret[k] = v
- end
- end
- ret.inject({}) do |n, (k,v)|
- n[k.downcase] = 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 = {})
- args.stringify_keys!
- return nil unless args.include?('just_hash')
- args.delete('just_hash')
- url = args['url'].kind_of?(URI) ? args['url'].path : 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 <rowset><row /><row /></rowset> 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
-
- @last_hash = compute_hash(opts.merge({:url => url, :just_hash => true })) # compute hash
-
-
- xml = check_exception(get_xml(url,opts))
- save_xml(xml) if @save_path
-
- return xml if just_xml
- return [] if xml.nil? # No XML document returned. We should panic.
-
- # Create the array of klass objects to return, assume we start with an empty set from the XML search for rows
- # and build from there.
- xml.search("//rowset/row").inject([]) { |ret,elem| ret << klass.new(elem) }
- end
-
- # Turns a hash into ?var=baz&bam=boo
- def format_url_request(opts)
- req = "?"
-
- opts.stringify_keys!
- opts.keys.sort.each do |key|
- req += "#{CGI.escape(key.to_s)}=#{CGI.escape(opts[key].to_s)}&" if opts[key]
- end
- req.chop # We are lazy and append a & to each pair even if it's the last one. FIXME: Don't do this.
- end
-
-
- # Gets the XML from a source.
- # Expects:
- # * source ( String | URI ) - If the +source+ is a String Reve will attempt to load the XML file from the local filesystem by the path specified as +source+. If the +source+ is a URI or is a String starting with http (lowercase) Reve will fetch it from that URI on the web.
- # * opts ( Hash ) - Hash of parameters for the request, such as keyid, vcode and such.
- # NOTE: To override the lowercase http -> URI rule make the HTTP part uppercase.
- def get_xml(source,opts)
- xml = ""
-
- # Let people still pass Strings starting with http.
- if source =~ /^http/
- source = URI.parse(source)
- end
-
- if source.kind_of?(URI)
- opts.merge({ :version => 2, :url => nil }) #the uri bit will now ignored in format_url_request
- req_args = format_url_request(opts)
- req = Net::HTTP::Get.new(source.path + req_args)
- req['User-Agent'] = @http_referer_agent || "Reve v#{@reve_version}; http://github.com/lisa/reve"
-
- res = nil
- response = nil
- 1.upto(@max_tries) do |try|
- begin
- # ||= to prevent making a new Net::HTTP object, the res = nil above should reset this for the next request.
- # the request needs to be here to rescue exceptions from it.
- http ||= Net::HTTP.new(source.host, source.port)
- http.use_ssl = true
- http.verify_mode = OpenSSL::SSL::VERIFY_NONE ##rework to use proper cert
- http.open_timeout = 3
- http.read_timeout = @timeout
- res = http.start {|http| http.request(req) }
- case res
- when Net::HTTPSuccess, Net::HTTPRedirection
- response = res.body
- end
- rescue Exception
- sleep 5
- next
- end
- break if response
- end
- raise Reve::Exceptions::ReveNetworkStatusException.new( (res.body rescue "No Response Body!") ) unless response
-
- xml = response
-
- # here ends test for URI
- elsif source.kind_of?(String)
- xml = File.open(source).read
- else
- raise Reve::Exceptions::ReveNetworkStatusException.new("Don't know how to deal with a #{source.class} XML source. I expect a URI or String")
- end
- @last_xml = xml
-
- xml
- end
-
- # Raises the proper exception (if there is one), otherwise it returns the
- # XML response.
- def check_exception(xml)
- x = Hpricot::XML(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
-
- def save_xml(xml)
- path = build_save_filename
- FileUtils.mkdir_p(File.dirname(path))
- File.open(path,'w') { |f| f.print xml.to_original_html }
- end
- def build_save_filename
- method = caller(3).first.match(/\`(.+)'/)[1] # Get the API method that's being called. This is called from save_xml -> process_query -> :real_method
- File.join(@save_path,@keyid.to_s,method,( @cached_until || Time.now.utc).to_i.to_s + '.xml')
- end
-
- # Returns an array of +klass+
- def pull_out_top_10_data(xml,klass,kind,field)
- xml.search("/eveapi/result/#{kind}/rowset[@name='#{field}']/row").inject([]) do |all,row|
- all << klass.new(row)
- all
- end
- end
+ #protected
+ include ProcessingHelpers
end
end
View
36 lib/reve/classes.rb
@@ -54,6 +54,8 @@ def initialize(elem) #:nodoc:
end
end
+
+
class EveFactionWarStat
attr_accessor :faction_participants, :faction_wars
attr_reader :kills_yesterday, :kills_last_week, :kills_total,
@@ -312,6 +314,40 @@ def initialize(elem) #:nodoc:
end
end
+ #
+ #Characters contracts
+ #
+ class Contracts
+ attr_reader :contractID, :issuerID, :issuerCorpID, :assigneeID
+ attr_reader :acceptorID, :startStationID, :endStationID, :type, :status
+ attr_reader :title, :forCorp, :availability, :dateIssued, :dateExpired, :dateAccepted
+ attr_reader :numDays, :dateCompleted, :price, :reward, :collateral, :buyout
+ attr_reader :volume
+ def initialize(elem) #:nodoc:
+ @contractID =elem['contractID'].to_i
+ @issuerID =elem['issuerID'].to_i
+ @issuerCorpID =elem['issuerCorpID'].to_i
+ @assigneeID =elem['assigneeID'].to_i
+ @acceptorID =elem['acceptorID'].to_i
+ @startStationID =elem['startStationID'].to_i
+ @endStationID =elem['endStationID'].to_i
+ @type =elem['type'].to_s
+ @status =elem['status'].to_s
+ @title =elem['title'].to_s
+ @forCorp =elem['forCorp'].to_i
+ @availability =elem['availability'].to_s
+ @dateIssued =elem['dateIssued'].to_time
+ @dateExpired =elem['dateExpired'].to_time
+ @dateAccepted =elem['dateAccepted'].to_time
+ @numDays =elem['numDays'].to_i
+ @dateCompleted =elem['dateCompleted'].to_time
+ @price =elem['price'].to_f
+ @reward =elem['reward'].to_f
+ @collateral =elem['collateral'].to_f
+ @buyout =elem['buyout'].to_f
+ @volume =elem['volume'].to_i
+ end
+ end
# A Skill has a RequiredAttribute, either a PrimaryAttribute or SecondaryAttribute, which both derrive from this.
# Attributes
# * name ( String ) - Name of the required Attribute
View
199 lib/reve/processing_helpers.rb
@@ -0,0 +1,199 @@
+module ProcessingHelpers
+
+# Helper method to handle nested assets
+ def recur_through_assets(rows)
+ assets = []
+ rows.each do |container|
+ unless container.empty?
+ asset_container = Reve::Classes::AssetContainer.new(container)
+ asset_container.assets = self.recur_through_assets(container.search("/rowset/row"))
+ assets << asset_container
+ else
+ assets << Reve::Classes::Asset.new(container)
+ end
+ end
+ assets
+ end
+
+ # Sets up the post fields for Net::HTTP::Get hash for process_query method.
+ # See also format_url_request
+ # TODO: Consider moving this whole thing into process_query to avoid
+ # calling this in every method!
+ def postfields(opts = {})
+ baseargs = { :characterid => @charid }
+ if @cak
+ baseargs[:keyid] = @keyid
+ baseargs[:vcode] = @key
+ else
+ baseargs[:userid] = @keyid
+ baseargs[:apikey] = @key
+ end
+ ret = opts.clone
+ baseargs.each do |k,v|
+ if ret[k].nil?
+ ret[k] = v
+ end
+ end
+ ret.inject({}) do |n, (k,v)|
+ n[k.downcase] = 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 = {})
+ args.stringify_keys!
+ return nil unless args.include?('just_hash')
+ args.delete('just_hash')
+ url = args['url'].kind_of?(URI) ? args['url'].path : 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 <rowset><row /><row /></rowset> 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
+
+ @last_hash = compute_hash(opts.merge({:url => url, :just_hash => true })) # compute hash
+
+
+ xml = check_exception(get_xml(url,opts))
+ save_xml(xml) if @save_path
+
+ return xml if just_xml
+ return [] if xml.nil? # No XML document returned. We should panic.
+
+ # Create the array of klass objects to return, assume we start with an empty set from the XML search for rows
+ # and build from there.
+ xml.search("//rowset/row").inject([]) { |ret,elem| ret << klass.new(elem) }
+ end
+
+ # Turns a hash into ?var=baz&bam=boo
+ def format_url_request(opts)
+ req = "?"
+
+ opts.stringify_keys!
+ opts.keys.sort.each do |key|
+ req += "#{CGI.escape(key.to_s)}=#{CGI.escape(opts[key].to_s)}&" if opts[key]
+ end
+ req.chop # We are lazy and append a & to each pair even if it's the last one. FIXME: Don't do this.
+ end
+
+
+ # Gets the XML from a source.
+ # Expects:
+ # * source ( String | URI ) - If the +source+ is a String Reve will attempt to load the XML file from the local filesystem by the path specified as +source+. If the +source+ is a URI or is a String starting with http (lowercase) Reve will fetch it from that URI on the web.
+ # * opts ( Hash ) - Hash of parameters for the request, such as keyid, vcode and such.
+ # NOTE: To override the lowercase http -> URI rule make the HTTP part uppercase.
+ def get_xml(source,opts)
+ xml = ""
+
+ # Let people still pass Strings starting with http.
+ if source =~ /^http/
+ source = URI.parse(source)
+ end
+
+ if source.kind_of?(URI)
+ opts.merge({ :version => 2, :url => nil }) #the uri bit will now ignored in format_url_request
+ req_args = format_url_request(opts)
+ req = Net::HTTP::Get.new(source.path + req_args)
+ req['User-Agent'] = @http_referer_agent || "Reve v#{@reve_version}; http://github.com/lisa/reve"
+
+ res = nil
+ response = nil
+ 1.upto(@max_tries) do |try|
+ begin
+ # ||= to prevent making a new Net::HTTP object, the res = nil above should reset this for the next request.
+ # the request needs to be here to rescue exceptions from it.
+ http ||= Net::HTTP.new(source.host, source.port)
+ http.use_ssl = true
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE ##rework to use proper cert
+ http.open_timeout = 3
+ http.read_timeout = @timeout
+ res = http.start {|http| http.request(req) }
+ case res
+ when Net::HTTPSuccess, Net::HTTPRedirection
+ response = res.body
+ end
+ rescue Exception
+ sleep 5
+ next
+ end
+ break if response
+ end
+ raise Reve::Exceptions::ReveNetworkStatusException.new( (res.body rescue "No Response Body!") ) unless response
+
+ xml = response
+
+ # here ends test for URI
+ elsif source.kind_of?(String)
+ xml = File.open(source).read
+ else
+ raise Reve::Exceptions::ReveNetworkStatusException.new("Don't know how to deal with a #{source.class} XML source. I expect a URI or String")
+ end
+ @last_xml = xml
+
+ xml
+ end
+
+ # Raises the proper exception (if there is one), otherwise it returns the
+ # XML response.
+ def check_exception(xml)
+ x = Hpricot::XML(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
+
+ def save_xml(xml)
+ path = build_save_filename
+ FileUtils.mkdir_p(File.dirname(path))
+ File.open(path,'w') { |f| f.print xml.to_original_html }
+ end
+
+ def build_save_filename
+ method = caller(3).first.match(/\`(.+)'/)[1] # Get the API method that's being called. This is called from save_xml -> process_query -> :real_method
+ File.join(@save_path,@keyid.to_s,method,( @cached_until || Time.now.utc).to_i.to_s + '.xml')
+ end
+
+ # Returns an array of +klass+
+ def pull_out_top_10_data(xml,klass,kind,field)
+ xml.search("/eveapi/result/#{kind}/rowset[@name='#{field}']/row").inject([]) do |all,row|
+ all << klass.new(row)
+ all
+ end
+ end
+
+end
View
0 test/integration_tests.rb → test/test_integration.rb
File renamed without changes.
View
14 test/test_reve.rb
@@ -1504,11 +1504,15 @@ def test_upcoming_calendar_events
#This test verifies that we can connect to the CPP API Server.
#Dont care what data comes back, just as long as data comes back.
#This is expensive and ugly, but important
- def test_End_to_End_Connectivity_Test
- api = Reve::API.new
- errors = api.errors
- assert_not_nil(errors.inspect)
- end
+
+ #This is commented out in this test filebecause is is being run to much and causing the server to get grumpy
+ #Run it from the test_integration.rb file instead and run it less often.
+
+ # def test_End_to_End_Connectivity_Test
+ # api = Reve::API.new
+ # errors = api.errors
+ # assert_not_nil(errors.inspect)
+ # end
#### All tests above this method.
protected
View
21 test/test_test.rb
@@ -0,0 +1,21 @@
+#begin
+ require 'simplecov'
+#rescue LoadError
+# gem install 'simplecov'
+#end
+SimpleCov.start
+require 'test/unit'
+#require 'fileutils' # for saving downloaded XML
+#$LOAD_PATH << './lib'
+#require 'reve'
+
+
+class TestReve < Test::Unit::TestCase
+
+
+#This test verifies that we can connect to the CPP API Server.
+#Dont care what data comes back, just as long as data comes back.
+ def test_occums_razor
+ puts "******* THIS TEST PASSES **********"
+ end
+end
View
11 test/xml/contracts.xml
@@ -0,0 +1,11 @@
+<?xml version='1.0' encoding='UTF-8'?>
+ <eveapi version="2">
+ <currentTime>2013-09-30 01:49:43</currentTime>
+ <result>
+ <rowset name="contractList" key="contractID" columns="contractID,issuerID,issuerCorpID,assigneeID,acceptorID,startStationID,endStationID,type,status,title,forCorp,availability,dateIssued,dateExpired,dateAccepted,numDays,dateCompleted,price,reward,collateral,buyout,volume">
+ <row contractID="1" issuerID="10" issuerCorpID="2" assigneeID="3" acceptorID="0" startStationID="60004615" endStationID="60004615" type="ItemExchange" status="Outstanding" title="Test_title" forCorp="0" availability="Private" dateIssued="2013-09-30 01:46:00" dateExpired="2013-10-01 01:46:00" dateAccepted="" numDays="0" dateCompleted="" price="0.00" reward="0.00" collateral="0.00" buyout="0.00" volume="5" />
+ <row contractID="2" issuerID="11" issuerCorpID="2" assigneeID="3" acceptorID="0" startStationID="60004615" endStationID="60004615" type="ItemExchange" status="Outstanding" title="Test Contract" forCorp="0" availability="Private" dateIssued="2013-09-30 01:42:45" dateExpired="2013-10-14 01:42:45" dateAccepted="" numDays="0" dateCompleted="" price="500.00" reward="0.00" collateral="0.00" buyout="0.00" volume="20" />
+ </rowset>
+ </result>
+ <cachedUntil>2013-09-30 02:03:43</cachedUntil>
+ </eveapi>

0 comments on commit b0e07e4

Please sign in to comment.
Something went wrong with that request. Please try again.