gmoney 0.0.2 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Manifest +3 -0
- data/README.rdoc +48 -4
- data/Rakefile +14 -10
- data/gmoney.gemspec +4 -4
- data/lib/extensions/fixnum.rb +13 -0
- data/lib/extensions/string.rb +52 -0
- data/lib/gmoney.rb +3 -1
- data/lib/gmoney/feed_parser.rb +1 -1
- data/lib/gmoney/portfolio.rb +30 -12
- data/lib/gmoney/position.rb +21 -10
- data/lib/gmoney/transaction.rb +14 -4
- data/spec/fixtures/position_feed_for_9_GOOG.xml +25 -0
- data/spec/fixtures/transaction_feed_for_GOOG_1.xml +17 -0
- data/spec/portfolio_spec.rb +50 -33
- data/spec/position_spec.rb +58 -29
- data/spec/spec_helper.rb +10 -0
- data/spec/string_spec.rb +39 -0
- data/spec/transaction_spec.rb +18 -7
- metadata +6 -2
data/Manifest
CHANGED
@@ -2,6 +2,7 @@ Manifest
|
|
2
2
|
README.rdoc
|
3
3
|
Rakefile
|
4
4
|
gmoney.gemspec
|
5
|
+
lib/extensions/fixnum.rb
|
5
6
|
lib/extensions/string.rb
|
6
7
|
lib/gmoney.rb
|
7
8
|
lib/gmoney/authentication_request.rb
|
@@ -22,9 +23,11 @@ spec/fixtures/default_portfolios_feed.xml
|
|
22
23
|
spec/fixtures/empty_portfolio_feed.xml
|
23
24
|
spec/fixtures/portfolio_9_feed.xml
|
24
25
|
spec/fixtures/portfolio_feed_with_returns.xml
|
26
|
+
spec/fixtures/position_feed_for_9_GOOG.xml
|
25
27
|
spec/fixtures/positions_feed_for_portfolio_14.xml
|
26
28
|
spec/fixtures/positions_feed_for_portfolio_9.xml
|
27
29
|
spec/fixtures/positions_feed_for_portfolio_9r.xml
|
30
|
+
spec/fixtures/transaction_feed_for_GOOG_1.xml
|
28
31
|
spec/fixtures/transactions_feed_for_GOOG.xml
|
29
32
|
spec/gmoney_spec.rb
|
30
33
|
spec/portfolio_feed_parser_spec.rb
|
data/README.rdoc
CHANGED
@@ -4,17 +4,61 @@ A gem for interacting with the Google Finance API
|
|
4
4
|
|
5
5
|
== Install
|
6
6
|
|
7
|
+
gem install gmoney
|
8
|
+
|
9
|
+
or
|
10
|
+
|
7
11
|
gem install jspradlin-gmoney --source http://gems.github.com
|
8
12
|
|
9
13
|
== Usage
|
10
14
|
|
11
|
-
|
15
|
+
Login
|
16
|
+
-----
|
17
|
+
|
18
|
+
> GMoney::GFSession.login('google username', 'password')
|
19
|
+
|
20
|
+
Portfolios
|
21
|
+
--------
|
22
|
+
|
23
|
+
> portfolios = GMoney::Portfolio.all #returns all of a users portfolios
|
24
|
+
> portfolio = GMoney::Portfolio.find(9) #returns a specific portfolio
|
25
|
+
|
26
|
+
> positions = portfolio.positions #returns an Array of the Positions held within a given portfolio
|
27
|
+
|
28
|
+
Positions
|
29
|
+
--------
|
12
30
|
|
13
|
-
|
31
|
+
> positions = GMoney::Position.find(9) #returns all of a users positions within a given portfolio, i.e. Portfolio "9"
|
32
|
+
> position = GMoney::Position.find("9/NASDAQ:GOOG") #returns a specific position within a given portfolio
|
33
|
+
|
34
|
+
> transactions = position.transactions #returns an Array of the Transactions within a given position
|
35
|
+
|
36
|
+
Transactions
|
37
|
+
--------
|
38
|
+
|
39
|
+
> transactions = GMoney::Transaction.find("9/NASDAQ:GOOG") #returns all of a users transactions within a given position
|
40
|
+
> transaction = GMoney::Transaction.find("9/NASDAQ:GOOG/2") #returns a specific transaction within a given position
|
41
|
+
|
42
|
+
----------
|
43
|
+
|
44
|
+
Options parameter
|
45
|
+
|
46
|
+
|
47
|
+
The following methods:
|
48
|
+
Portfolio.all
|
49
|
+
Portfolio.find
|
50
|
+
portfolio.positions #where portfolio is an instance of the Portfolio class
|
14
51
|
|
15
|
-
|
52
|
+
Position.find
|
53
|
+
position.transactions #where position is an instance of the Position class
|
54
|
+
|
55
|
+
Transaction.find
|
56
|
+
|
57
|
+
all take a hash as an optional last parameter. Valid values for this hash are:
|
16
58
|
|
17
|
-
|
59
|
+
* :returns - By default Google does not return a comprehensive list of returns data. Set this parameter to true to access this information.
|
60
|
+
* :refresh - GMoney caches the data returned from Google by default. Set :refresh => true if you would like to get the latest data.
|
61
|
+
* :eager - GMoney lazily loads children data (i.e. positions for a given Portfolio). If you would like to load all of this data at once set :eager => true
|
18
62
|
|
19
63
|
== License
|
20
64
|
|
data/Rakefile
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
require 'rubygems'
|
2
2
|
require 'rake'
|
3
3
|
require 'echoe'
|
4
|
-
require 'spec/rake/spectask'
|
4
|
+
require 'spec/rake/spectask'
|
5
|
+
require 'lib/gmoney'
|
5
6
|
|
6
|
-
task :default => :
|
7
|
+
task :default => :spec
|
7
8
|
|
8
9
|
desc "Run the gmoney specs"
|
9
|
-
task :
|
10
|
+
task :spec do
|
10
11
|
Spec::Rake::SpecTask.new do |t|
|
11
12
|
t.spec_files = FileList['spec/**/*_spec.rb']
|
12
13
|
t.rcov = true
|
@@ -15,11 +16,14 @@ task :specs do
|
|
15
16
|
end
|
16
17
|
end
|
17
18
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
19
|
+
desc "Gemify GMoney"
|
20
|
+
namespace :gemify do
|
21
|
+
Echoe.new('gmoney', GMoney.version) do |p|
|
22
|
+
p.description = "A gem for interacting with the Google Finance API"
|
23
|
+
p.url = "http://github.com/jspradlin/gmoney"
|
24
|
+
p.author = "Justin Spradlin"
|
25
|
+
p.email = "jspradlin@gmail.com"
|
26
|
+
p.ignore_pattern = ["coverage/*", "*~"]
|
27
|
+
p.development_dependencies = []
|
28
|
+
end
|
25
29
|
end
|
data/gmoney.gemspec
CHANGED
@@ -2,15 +2,15 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = %q{gmoney}
|
5
|
-
s.version = "0.0
|
5
|
+
s.version = "0.1.0"
|
6
6
|
|
7
7
|
s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
|
8
8
|
s.authors = ["Justin Spradlin"]
|
9
|
-
s.date = %q{2009-
|
9
|
+
s.date = %q{2009-10-01}
|
10
10
|
s.description = %q{A gem for interacting with the Google Finance API}
|
11
11
|
s.email = %q{jspradlin@gmail.com}
|
12
|
-
s.extra_rdoc_files = ["README.rdoc", "lib/extensions/string.rb", "lib/gmoney.rb", "lib/gmoney/authentication_request.rb", "lib/gmoney/feed_parser.rb", "lib/gmoney/gf_request.rb", "lib/gmoney/gf_response.rb", "lib/gmoney/gf_service.rb", "lib/gmoney/gf_session.rb", "lib/gmoney/portfolio.rb", "lib/gmoney/portfolio_feed_parser.rb", "lib/gmoney/position.rb", "lib/gmoney/position_feed_parser.rb", "lib/gmoney/transaction.rb", "lib/gmoney/transaction_feed_parser.rb"]
|
13
|
-
s.files = ["Manifest", "README.rdoc", "Rakefile", "gmoney.gemspec", "lib/extensions/string.rb", "lib/gmoney.rb", "lib/gmoney/authentication_request.rb", "lib/gmoney/feed_parser.rb", "lib/gmoney/gf_request.rb", "lib/gmoney/gf_response.rb", "lib/gmoney/gf_service.rb", "lib/gmoney/gf_session.rb", "lib/gmoney/portfolio.rb", "lib/gmoney/portfolio_feed_parser.rb", "lib/gmoney/position.rb", "lib/gmoney/position_feed_parser.rb", "lib/gmoney/transaction.rb", "lib/gmoney/transaction_feed_parser.rb", "spec/authentication_request_spec.rb", "spec/fixtures/cacert.pem", "spec/fixtures/default_portfolios_feed.xml", "spec/fixtures/empty_portfolio_feed.xml", "spec/fixtures/portfolio_9_feed.xml", "spec/fixtures/portfolio_feed_with_returns.xml", "spec/fixtures/positions_feed_for_portfolio_14.xml", "spec/fixtures/positions_feed_for_portfolio_9.xml", "spec/fixtures/positions_feed_for_portfolio_9r.xml", "spec/fixtures/transactions_feed_for_GOOG.xml", "spec/gmoney_spec.rb", "spec/portfolio_feed_parser_spec.rb", "spec/portfolio_spec.rb", "spec/position_feed_parser_spec.rb", "spec/position_spec.rb", "spec/request_spec.rb", "spec/response_spec.rb", "spec/service_spec.rb", "spec/session_spec.rb", "spec/spec.opts", "spec/spec_helper.rb", "spec/string_spec.rb", "spec/transaction_feed_parser_spec.rb", "spec/transaction_spec.rb"]
|
12
|
+
s.extra_rdoc_files = ["README.rdoc", "lib/extensions/fixnum.rb", "lib/extensions/string.rb", "lib/gmoney.rb", "lib/gmoney/authentication_request.rb", "lib/gmoney/feed_parser.rb", "lib/gmoney/gf_request.rb", "lib/gmoney/gf_response.rb", "lib/gmoney/gf_service.rb", "lib/gmoney/gf_session.rb", "lib/gmoney/portfolio.rb", "lib/gmoney/portfolio_feed_parser.rb", "lib/gmoney/position.rb", "lib/gmoney/position_feed_parser.rb", "lib/gmoney/transaction.rb", "lib/gmoney/transaction_feed_parser.rb"]
|
13
|
+
s.files = ["Manifest", "README.rdoc", "Rakefile", "gmoney.gemspec", "lib/extensions/fixnum.rb", "lib/extensions/string.rb", "lib/gmoney.rb", "lib/gmoney/authentication_request.rb", "lib/gmoney/feed_parser.rb", "lib/gmoney/gf_request.rb", "lib/gmoney/gf_response.rb", "lib/gmoney/gf_service.rb", "lib/gmoney/gf_session.rb", "lib/gmoney/portfolio.rb", "lib/gmoney/portfolio_feed_parser.rb", "lib/gmoney/position.rb", "lib/gmoney/position_feed_parser.rb", "lib/gmoney/transaction.rb", "lib/gmoney/transaction_feed_parser.rb", "spec/authentication_request_spec.rb", "spec/fixtures/cacert.pem", "spec/fixtures/default_portfolios_feed.xml", "spec/fixtures/empty_portfolio_feed.xml", "spec/fixtures/portfolio_9_feed.xml", "spec/fixtures/portfolio_feed_with_returns.xml", "spec/fixtures/position_feed_for_9_GOOG.xml", "spec/fixtures/positions_feed_for_portfolio_14.xml", "spec/fixtures/positions_feed_for_portfolio_9.xml", "spec/fixtures/positions_feed_for_portfolio_9r.xml", "spec/fixtures/transaction_feed_for_GOOG_1.xml", "spec/fixtures/transactions_feed_for_GOOG.xml", "spec/gmoney_spec.rb", "spec/portfolio_feed_parser_spec.rb", "spec/portfolio_spec.rb", "spec/position_feed_parser_spec.rb", "spec/position_spec.rb", "spec/request_spec.rb", "spec/response_spec.rb", "spec/service_spec.rb", "spec/session_spec.rb", "spec/spec.opts", "spec/spec_helper.rb", "spec/string_spec.rb", "spec/transaction_feed_parser_spec.rb", "spec/transaction_spec.rb"]
|
14
14
|
s.homepage = %q{http://github.com/jspradlin/gmoney}
|
15
15
|
s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Gmoney", "--main", "README.rdoc"]
|
16
16
|
s.require_paths = ["lib"]
|
data/lib/extensions/string.rb
CHANGED
@@ -1,4 +1,16 @@
|
|
1
1
|
class String
|
2
|
+
class PortfolioParseError < StandardError; end
|
3
|
+
class PositionParseError < StandardError; end
|
4
|
+
class TransactionParseError < StandardError; end
|
5
|
+
|
6
|
+
@@portfolio_re = /\d+/
|
7
|
+
@@portfolio_re_in = /^\d+$/
|
8
|
+
@@position_re = /\d+\/[a-zA-Z]+:[a-zA-Z]+/
|
9
|
+
@@position_re_in = /^\d+\/[a-zA-Z]+:[a-zA-Z]+$/
|
10
|
+
@@transaction_re = /\d+\/[a-zA-Z]+:[a-zA-Z]+\/\d+/
|
11
|
+
@@transaction_re_in = /^\d+\/[a-zA-Z]+:[a-zA-Z]+\/\d+$/
|
12
|
+
|
13
|
+
|
2
14
|
def camel_to_us
|
3
15
|
add_us = gsub(/(.)([A-Z][a-z]+)/, '\1_\2')
|
4
16
|
add_us.gsub(/([a-z0-9])([A-Z])/, '\1_\2').downcase
|
@@ -7,4 +19,44 @@ class String
|
|
7
19
|
def is_numeric?
|
8
20
|
Float self rescue false
|
9
21
|
end
|
22
|
+
|
23
|
+
def portfolio_feed_id
|
24
|
+
self[self.rindex('/')+1..-1]
|
25
|
+
end
|
26
|
+
|
27
|
+
def position_feed_id
|
28
|
+
portfolio = self[self.rindex('portfolios/')+11..index('/positions')-1]
|
29
|
+
position = self[rindex('/')+1..-1]
|
30
|
+
"#{portfolio}/#{position}"
|
31
|
+
end
|
32
|
+
|
33
|
+
def portfolio_id
|
34
|
+
if self[@@transaction_re_in] || self[@@position_re_in] || self[@@portfolio_re_in]
|
35
|
+
self[@@portfolio_re]
|
36
|
+
else
|
37
|
+
raise PortfolioParseError
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def position_id
|
42
|
+
if self[@@portfolio_re_in]
|
43
|
+
""
|
44
|
+
elsif self[@@position_re_in]
|
45
|
+
self[self.index('/')+1..-1]
|
46
|
+
elsif self[@@transaction_re_in]
|
47
|
+
self[self.index('/')+1..self.rindex('/')-1]
|
48
|
+
else
|
49
|
+
raise PositionParseError
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def transaction_id
|
54
|
+
if self[@@position_re_in]
|
55
|
+
""
|
56
|
+
elsif self[@@transaction_re_in]
|
57
|
+
self[self.rindex('/')+1..-1]
|
58
|
+
else
|
59
|
+
raise TransactionParseError
|
60
|
+
end
|
61
|
+
end
|
10
62
|
end
|
data/lib/gmoney.rb
CHANGED
@@ -10,6 +10,7 @@ require 'net/http'
|
|
10
10
|
require 'net/https'
|
11
11
|
require 'rexml/document'
|
12
12
|
|
13
|
+
require 'extensions/fixnum'
|
13
14
|
require 'extensions/string'
|
14
15
|
|
15
16
|
require 'gmoney/authentication_request'
|
@@ -26,9 +27,10 @@ require 'gmoney/transaction'
|
|
26
27
|
require 'gmoney/transaction_feed_parser'
|
27
28
|
|
28
29
|
module GMoney
|
29
|
-
VERSION = '0.0
|
30
|
+
VERSION = '0.1.0'
|
30
31
|
GF_URL = "https://finance.google.com/finance"
|
31
32
|
GF_FEED_URL = "#{GF_URL}/feeds/default"
|
33
|
+
GF_PORTFOLIO_FEED_URL = "#{GF_FEED_URL}/portfolios"
|
32
34
|
|
33
35
|
HTTPOK = 200
|
34
36
|
HTTPCreated = 201
|
data/lib/gmoney/feed_parser.rb
CHANGED
@@ -10,7 +10,7 @@ module GMoney
|
|
10
10
|
target.instance_variable_set("@#{name.camel_to_us}", value)
|
11
11
|
end
|
12
12
|
|
13
|
-
doc.elements.each('
|
13
|
+
doc.elements.each('//entry') do |parsed_entry|
|
14
14
|
finance_object = feed_class.new
|
15
15
|
finance_data = parsed_entry.elements["gf:#{feed_class_string}Data"]
|
16
16
|
|
data/lib/gmoney/portfolio.rb
CHANGED
@@ -2,19 +2,34 @@ module GMoney
|
|
2
2
|
class Portfolio
|
3
3
|
class PortfolioRequestError < StandardError;end
|
4
4
|
|
5
|
-
attr_accessor :title, :currency_code
|
5
|
+
attr_accessor :title, :currency_code
|
6
6
|
|
7
7
|
attr_reader :id, :feed_link, :updated, :gain_percentage, :return1w, :return4w, :return3m,
|
8
8
|
:return_ytd, :return1y, :return3y, :return5y, :return_overall,
|
9
9
|
:cost_basis, :days_gain, :gain, :market_value
|
10
|
+
|
11
|
+
def self.all(options = {})
|
12
|
+
retreive_portfolios(:all, options)
|
13
|
+
end
|
10
14
|
|
11
|
-
def
|
12
|
-
|
15
|
+
def self.find(id, options = {})
|
16
|
+
retreive_portfolios(id, options)
|
13
17
|
end
|
14
18
|
|
15
|
-
def
|
16
|
-
|
17
|
-
|
19
|
+
def positions(options = {})
|
20
|
+
if options[:refresh]
|
21
|
+
@positions = Position.find(@id.portfolio_feed_id, options)
|
22
|
+
else
|
23
|
+
@positions ||= Position.find(@id.portfolio_feed_id, options)
|
24
|
+
end
|
25
|
+
|
26
|
+
@positions.is_a?(Array) ? @positions : [@positions]
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.retreive_portfolios(id, options = {})
|
30
|
+
url = GF_PORTFOLIO_FEED_URL
|
31
|
+
url += "/#{id}" if id != :all
|
32
|
+
url += "?returns=true" if options[:returns]
|
18
33
|
portfolios = []
|
19
34
|
|
20
35
|
response = GFService.send_request(GFRequest.new(url, :headers => {"Authorization" => "GoogleLogin auth=#{GFSession.auth_token}"}))
|
@@ -22,13 +37,16 @@ module GMoney
|
|
22
37
|
if response.status_code == HTTPOK
|
23
38
|
portfolios = PortfolioFeedParser.parse_portfolio_feed(response.body)
|
24
39
|
else
|
25
|
-
raise PortfolioRequestError
|
40
|
+
raise PortfolioRequestError, response.body
|
26
41
|
end
|
27
42
|
|
28
|
-
portfolios.each
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
43
|
+
portfolios.each { |p| p.instance_variable_set("@positions", p.positions(options))} if options[:eager]
|
44
|
+
|
45
|
+
return portfolios[0] if portfolios.size == 1
|
46
|
+
|
47
|
+
portfolios
|
48
|
+
end
|
49
|
+
|
50
|
+
private_class_method :retreive_portfolios
|
33
51
|
end
|
34
52
|
end
|
data/lib/gmoney/position.rb
CHANGED
@@ -1,33 +1,44 @@
|
|
1
1
|
module GMoney
|
2
2
|
class Position
|
3
3
|
class PositionRequestError < StandardError; end
|
4
|
-
attr_accessor :transactions
|
5
|
-
|
6
4
|
attr_reader :id, :updated, :title, :feed_link, :exchange, :symbol, :shares,
|
7
5
|
:full_name, :gain_percentage, :return1w, :return4w, :return3m,
|
8
6
|
:return_ytd, :return1y, :return3y, :return5y, :return_overall,
|
9
7
|
:cost_basis, :days_gain, :gain, :market_value
|
10
8
|
|
11
|
-
def
|
12
|
-
|
9
|
+
def self.find(id, options={})
|
10
|
+
find_by_url("#{GF_PORTFOLIO_FEED_URL}/#{id.portfolio_id}/positions/#{id.position_id}", options)
|
11
|
+
end
|
12
|
+
|
13
|
+
def transactions(options={})
|
14
|
+
if options[:refresh]
|
15
|
+
@transactions = Transaction.find(@id.position_feed_id, options)
|
16
|
+
else
|
17
|
+
@transactions ||= Transaction.find(@id.position_feed_id, options)
|
18
|
+
end
|
19
|
+
|
20
|
+
@transactions.is_a?(Array) ? @transactions : [@transactions]
|
13
21
|
end
|
14
22
|
|
15
23
|
def self.find_by_url(url, options = {})
|
16
24
|
positions = []
|
17
|
-
url += "?returns=true" if options[:
|
25
|
+
url += "?returns=true" if options[:returns]
|
18
26
|
|
19
27
|
response = GFService.send_request(GFRequest.new(url, :headers => {"Authorization" => "GoogleLogin auth=#{GFSession.auth_token}"}))
|
20
28
|
|
21
29
|
if response.status_code == HTTPOK
|
22
30
|
positions = PositionFeedParser.parse_position_feed(response.body) if response.status_code == 200
|
23
31
|
else
|
24
|
-
raise PositionRequestError
|
32
|
+
raise PositionRequestError, response.body
|
25
33
|
end
|
26
34
|
|
27
|
-
positions.each
|
28
|
-
|
29
|
-
|
30
|
-
|
35
|
+
positions.each { |p| p.instance_variable_set("@transactions", p.transactions(options))} if options[:eager]
|
36
|
+
|
37
|
+
return positions[0] if positions.size == 1
|
38
|
+
|
39
|
+
positions
|
31
40
|
end
|
41
|
+
|
42
|
+
private_class_method :find_by_url
|
32
43
|
end
|
33
44
|
end
|
data/lib/gmoney/transaction.rb
CHANGED
@@ -6,16 +6,26 @@ module GMoney
|
|
6
6
|
|
7
7
|
attr_accessor :type, :date, :shares, :notes, :commission, :price
|
8
8
|
|
9
|
-
def self.
|
9
|
+
def self.find(id, options={})
|
10
|
+
find_by_url("#{GF_PORTFOLIO_FEED_URL}/#{id.portfolio_id}/positions/#{id.position_id}/transactions/#{id.transaction_id}", options)
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.find_by_url(url, options={})
|
10
14
|
transactions = []
|
11
15
|
|
12
16
|
response = GFService.send_request(GFRequest.new(url, :headers => {"Authorization" => "GoogleLogin auth=#{GFSession.auth_token}"}))
|
13
17
|
|
14
18
|
if response.status_code == HTTPOK
|
15
|
-
TransactionFeedParser.parse_transaction_feed(response.body)
|
19
|
+
transactions = TransactionFeedParser.parse_transaction_feed(response.body)
|
16
20
|
else
|
17
|
-
raise TransactionRequestError
|
21
|
+
raise TransactionRequestError, response.body
|
18
22
|
end
|
19
|
-
|
23
|
+
|
24
|
+
return transactions[0] if transactions.size == 1
|
25
|
+
|
26
|
+
transactions
|
27
|
+
end
|
28
|
+
|
29
|
+
private_class_method :find_by_url
|
20
30
|
end
|
21
31
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
<?xml version='1.0' encoding='utf-8'?>
|
2
|
+
<entry xmlns='http://www.w3.org/2005/Atom' xmlns:gf='http://schemas.google.com/finance/2007' xmlns:gd='http://schemas.google.com/g/2005'>
|
3
|
+
<id>http://finance.google.com/finance/feeds/user@example.com/portfolios/9/positions/NASDAQ:GOOG</id>
|
4
|
+
<updated>2009-09-27T22:35:35.000Z</updated>
|
5
|
+
<category scheme='http://schemas.google.com/g/2005#kind' term='http://schemas.google.com/finance/2007#position' />
|
6
|
+
<title type='text'>Google Inc.</title>
|
7
|
+
<link rel='self' type='application/atom+xml' href='http://finance.google.com/finance/feeds/default/portfolios/9/positions/NASDAQ%3AGOOG' />
|
8
|
+
<gd:feedLink href='http://finance.google.com/finance/feeds/user@example.com/portfolios/9/positions/NASDAQ:GOOG/transactions' />
|
9
|
+
<gf:positionData gainPercentage='0.1742898491' return1w='0.002075448663' return1y='0.1665490533' return3m='0.1665490533' return3y='0.1665490533' return4w='0.1665490533' return5y='0.1665490533' returnOverall='0.1665490533' returnYTD='0.1665490533'
|
10
|
+
shares='325.0'>
|
11
|
+
<gf:costBasis>
|
12
|
+
<gd:money amount='136300.25' currencyCode='USD' />
|
13
|
+
</gf:costBasis>
|
14
|
+
<gf:daysGain>
|
15
|
+
<gd:money amount='-1394.24285' currencyCode='USD' />
|
16
|
+
</gf:daysGain>
|
17
|
+
<gf:gain>
|
18
|
+
<gd:money amount='23755.75' currencyCode='USD' />
|
19
|
+
</gf:gain>
|
20
|
+
<gf:marketValue>
|
21
|
+
<gd:money amount='160056.0' currencyCode='USD' />
|
22
|
+
</gf:marketValue>
|
23
|
+
</gf:positionData>
|
24
|
+
<gf:symbol exchange='NASDAQ' fullName='Google Inc.' symbol='GOOG' />
|
25
|
+
</entry>
|
@@ -0,0 +1,17 @@
|
|
1
|
+
<?xml version='1.0' encoding='utf-8'?>
|
2
|
+
<entry xmlns='http://www.w3.org/2005/Atom' xmlns:gf='http://schemas.google.com/finance/2007' xmlns:gd='http://schemas.google.com/g/2005'>
|
3
|
+
<id>http://finance.google.com/finance/feeds/user@example.com/portfolios/9/positions/NASDAQ:GOOG/transactions/2</id>
|
4
|
+
<updated>2009-09-12T18:22:44.000Z</updated>
|
5
|
+
<category scheme='http://schemas.google.com/g/2005#kind' term='http://schemas.google.com/finance/2007#transaction' />
|
6
|
+
<title type='text'>2</title>
|
7
|
+
<link rel='self' type='application/atom+xml' href='http://finance.google.com/finance/feeds/default/portfolios/9/positions/NASDAQ%3AGOOG/transactions/2' />
|
8
|
+
<link rel='edit' type='application/atom+xml' href='http://finance.google.com/finance/feeds/default/portfolios/9/positions/NASDAQ%3AGOOG/transactions/2' />
|
9
|
+
<gf:transactionData date='2009-09-01T00:00:00.000' shares='100.0' type='Buy'>
|
10
|
+
<gf:commission>
|
11
|
+
<gd:money amount='50.0' currencyCode='USD' />
|
12
|
+
</gf:commission>
|
13
|
+
<gf:price>
|
14
|
+
<gd:money amount='400.0' currencyCode='USD' />
|
15
|
+
</gf:price>
|
16
|
+
</gf:transactionData>
|
17
|
+
</entry>
|
data/spec/portfolio_spec.rb
CHANGED
@@ -17,7 +17,7 @@ describe GMoney::Portfolio do
|
|
17
17
|
@gf_response = GMoney::GFResponse.new
|
18
18
|
@gf_response.status_code = 200
|
19
19
|
@gf_response.body = @default_feed
|
20
|
-
@positions =
|
20
|
+
@positions = nil
|
21
21
|
end
|
22
22
|
|
23
23
|
it "should return all Portfolios when status_code is 200" do
|
@@ -27,70 +27,87 @@ describe GMoney::Portfolio do
|
|
27
27
|
portfolios.size.should be_eql(3)
|
28
28
|
end
|
29
29
|
|
30
|
-
it "should return a portfolio with returns data is :
|
30
|
+
it "should return a portfolio with returns data is :returns == true" do
|
31
31
|
@url = 'https://finance.google.com/finance/feeds/default/portfolios?returns=true'
|
32
32
|
@gf_response.body = @feed_with_returns
|
33
33
|
|
34
|
-
portfolios = portfolio_helper(@url, {:
|
34
|
+
portfolios = portfolio_helper(@url, :all, {:returns => true})
|
35
35
|
|
36
36
|
portfolios.size.should be_eql(3)
|
37
37
|
portfolios[0].cost_basis.should be_eql(2500.00)
|
38
38
|
portfolios[0].gain_percentage.should be_eql(28.3636)
|
39
39
|
portfolios[0].return4w.should be_eql(-0.1670616114)
|
40
|
-
|
41
40
|
portfolios[2].cost_basis.should be_nil
|
42
41
|
end
|
43
42
|
|
44
43
|
it "should raise an error if the portfolio request does not return an OK status code" do
|
45
|
-
@gf_response.status_code =
|
46
|
-
|
44
|
+
@gf_response.status_code = 401
|
47
45
|
lambda { portfolio_helper(@url) }.should raise_error(GMoney::Portfolio::PortfolioRequestError)
|
48
46
|
end
|
49
47
|
|
50
|
-
it "should
|
48
|
+
it "should raise an error with an error message when the user requests an invalid portfolio" do
|
49
|
+
@url += '/235234'
|
50
|
+
@gf_response.status_code = 404
|
51
|
+
@gf_response.body = "No portfolio exists with pid 235234"
|
52
|
+
|
53
|
+
lambda { portfolio_helper(@url, 235234) }.should raise_error(GMoney::Portfolio::PortfolioRequestError, @gf_response.body)
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should return the default 'My Portfolio' portfolio when a user has not defined his own portfolios" do
|
51
57
|
@gf_response.body = @empty_feed
|
52
|
-
|
53
|
-
|
54
|
-
portfolios.size.should be_eql(1)
|
55
|
-
portfolios[0].title.should be_eql('My Portfolio')
|
58
|
+
portfolio = portfolio_helper(@url)
|
59
|
+
portfolio.title.should be_eql('My Portfolio')
|
56
60
|
end
|
57
61
|
|
58
|
-
|
59
|
-
it "should return a specific portfolio is the request a specific portfolio" do
|
62
|
+
it "should return a specific portfolio if the user requests a specific portfolio" do
|
60
63
|
@url += '/9'
|
61
64
|
@gf_request.url = @url
|
62
65
|
|
63
66
|
@gf_response.body = @portfolio_9_feed
|
64
|
-
|
67
|
+
portfolio = portfolio_helper(@url, 9)
|
68
|
+
portfolio.title.should be_eql('GMoney Test')
|
69
|
+
end
|
70
|
+
|
71
|
+
it "should grab the latest positions when :refresh is used." do
|
72
|
+
@url += '/9'
|
73
|
+
@gf_request.url = @url
|
74
|
+
@positions = [GMoney::Position.new, GMoney::Position.new]
|
75
|
+
|
76
|
+
@gf_response.body = @portfolio_9_feed
|
77
|
+
portfolio = portfolio_helper(@url, 9)
|
78
|
+
|
79
|
+
GMoney::Position.should_receive(:find).with("9", {:refresh => true}).and_return(@positions)
|
65
80
|
|
66
|
-
|
67
|
-
portfolios[0].title.should be_eql('GMoney Test')
|
81
|
+
portfolio.positions(:refresh => true).size.should be_eql(2)
|
68
82
|
end
|
69
|
-
|
83
|
+
|
84
|
+
it "should use the cached portfolios when :refresh is not used (and the portfolios have already been set)" do
|
85
|
+
@url += '/9'
|
86
|
+
@gf_request.url = @url
|
87
|
+
@positions = [GMoney::Position.new, GMoney::Position.new]
|
88
|
+
|
89
|
+
@gf_response.body = @portfolio_9_feed
|
90
|
+
portfolio = portfolio_helper(@url, 9, :eager => true)
|
91
|
+
|
92
|
+
GMoney::Position.should_not_receive(:find).with(9, :eager => true)
|
93
|
+
portfolio.positions.size.should be_eql(2)
|
94
|
+
end
|
95
|
+
|
70
96
|
|
71
|
-
def portfolio_helper(url, options = {})
|
97
|
+
def portfolio_helper(url, id = nil, options = {})
|
72
98
|
GMoney::GFSession.should_receive(:auth_token).and_return('toke')
|
73
99
|
|
74
100
|
GMoney::GFRequest.should_receive(:new).with(url, :headers => {"Authorization" => "GoogleLogin auth=toke"}).and_return(@gf_request)
|
75
101
|
|
76
102
|
GMoney::GFService.should_receive(:send_request).with(@gf_request).and_return(@gf_response)
|
77
103
|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
104
|
+
if options[:eager]
|
105
|
+
feed_ids = get_feed_ids(@gf_response.body)
|
106
|
+
feed_ids.each do |feed_id|
|
107
|
+
GMoney::Position.should_receive(:find).with(feed_id.portfolio_feed_id, options).any_number_of_times.and_return(@positions)
|
108
|
+
end
|
82
109
|
end
|
83
110
|
|
84
|
-
GMoney::Portfolio.all(options)
|
85
|
-
end
|
86
|
-
|
87
|
-
def get_position_urls(feed)
|
88
|
-
doc = REXML::Document.new(feed)
|
89
|
-
feed_links = []
|
90
|
-
|
91
|
-
doc.elements.each('feed/entry') do |parsed_entry|
|
92
|
-
feed_links << parsed_entry.elements['gd:feedLink'].attributes['href']
|
93
|
-
end
|
94
|
-
feed_links
|
111
|
+
portfolios = id ? GMoney::Portfolio.find(id, options) : GMoney::Portfolio.all(options)
|
95
112
|
end
|
96
113
|
end
|
data/spec/position_spec.rb
CHANGED
@@ -5,12 +5,13 @@ describe GMoney::Position do
|
|
5
5
|
@default_feed = File.read('spec/fixtures/positions_feed_for_portfolio_9.xml')
|
6
6
|
@feed_with_returns = File.read('spec/fixtures/positions_feed_for_portfolio_9r.xml')
|
7
7
|
@empty_feed = File.read('spec/fixtures/positions_feed_for_portfolio_14.xml')
|
8
|
+
@feed_for_port9_goog = File.read('spec/fixtures/position_feed_for_9_GOOG.xml')
|
8
9
|
end
|
9
10
|
|
10
11
|
before(:each) do
|
11
|
-
@
|
12
|
+
@portfolio_id = '9'
|
12
13
|
|
13
|
-
@gf_request = GMoney::GFRequest.new(@
|
14
|
+
@gf_request = GMoney::GFRequest.new(@portfolio_id)
|
14
15
|
@gf_request.method = :get
|
15
16
|
|
16
17
|
@gf_response = GMoney::GFResponse.new
|
@@ -21,15 +22,15 @@ describe GMoney::Position do
|
|
21
22
|
|
22
23
|
it "should return all Positions when status_code is 200" do
|
23
24
|
@gf_response.body = @default_feed
|
24
|
-
positions = position_helper(@
|
25
|
+
positions = position_helper(@portfolio_id)
|
25
26
|
|
26
27
|
positions.size.should be_eql(5)
|
27
28
|
end
|
28
29
|
|
29
|
-
it "should return a position with returns data is :
|
30
|
+
it "should return a position with returns data is :returns == true" do
|
30
31
|
@gf_response.body = @feed_with_returns
|
31
32
|
|
32
|
-
positions = position_helper(@
|
33
|
+
positions = position_helper(@portfolio_id, {:returns => true})
|
33
34
|
|
34
35
|
positions.size.should be_eql(5)
|
35
36
|
positions[0].cost_basis.should be_eql(615.00)
|
@@ -40,41 +41,69 @@ describe GMoney::Position do
|
|
40
41
|
end
|
41
42
|
|
42
43
|
it "should raise an error if the position request does not return an OK status code" do
|
43
|
-
@gf_response.status_code = 404
|
44
|
-
|
45
|
-
lambda { position_helper(@url) }.should raise_error(GMoney::Position::PositionRequestError)
|
44
|
+
@gf_response.status_code = 404
|
45
|
+
lambda { position_helper(@portfolio_id) }.should raise_error(GMoney::Position::PositionRequestError)
|
46
46
|
end
|
47
47
|
|
48
|
-
|
49
|
-
|
48
|
+
it "should return a specific position if the user requests a specific position" do
|
49
|
+
@portfolio_id = '9/NASDAG:GOOG'
|
50
|
+
@gf_response.body = @feed_for_port9_goog
|
51
|
+
|
52
|
+
position = position_helper(@portfolio_id)
|
53
|
+
|
54
|
+
position.cost_basis.should be_eql(136300.25)
|
55
|
+
position.return1w.should be_eql(0.002075448663)
|
50
56
|
end
|
51
|
-
|
57
|
+
|
58
|
+
|
59
|
+
it "should raise an error message when a user request an invalid Position" do
|
60
|
+
@portfolio_id = '9/NASDAG:ASDF'
|
61
|
+
@gf_response.status_code = 404
|
62
|
+
@gf_response.body = "No position exists with ticker NASDAQ:ASDF"
|
52
63
|
|
53
|
-
|
64
|
+
lambda { position_helper(@portfolio_id)}.should raise_error(GMoney::Position::PositionRequestError, @gf_response.body)
|
65
|
+
end
|
66
|
+
|
67
|
+
it "should grab the latest transactions when :refresh is used" do
|
68
|
+
@position_id = '9/NASDAG:GOOG'
|
69
|
+
@transactions = [GMoney::Transaction.new, GMoney::Transaction.new, GMoney::Transaction.new]
|
70
|
+
|
71
|
+
@gf_response.body = @feed_for_port9_goog
|
72
|
+
position = position_helper(@position_id, :refresh => true)
|
73
|
+
|
74
|
+
GMoney::Transaction.should_receive(:find).with(position.id.position_feed_id, {:refresh => true}).and_return(@transactions)
|
75
|
+
|
76
|
+
position.transactions(:refresh => true).size.should be_eql(3)
|
77
|
+
end
|
78
|
+
|
79
|
+
it "should grab cached transactions when :refresh is not used" do
|
80
|
+
@position_id = '9/NASDAG:GOOG'
|
81
|
+
@transactions = [GMoney::Transaction.new, GMoney::Transaction.new, GMoney::Transaction.new]
|
82
|
+
|
83
|
+
@gf_response.body = @feed_for_port9_goog
|
84
|
+
position = position_helper(@position_id, :eager => true)
|
85
|
+
|
86
|
+
GMoney::Transaction.should_not_receive(:find).with(position.id.position_feed_id, {:eager => true})
|
87
|
+
position.transactions(:eager => true).size.should be_eql(3)
|
88
|
+
end
|
89
|
+
|
90
|
+
def position_helper(id, options = {})
|
54
91
|
GMoney::GFSession.should_receive(:auth_token).and_return('toke')
|
55
92
|
|
56
|
-
|
93
|
+
url = "#{GMoney::GF_PORTFOLIO_FEED_URL}/#{id.portfolio_id}/positions/#{id.position_id}"
|
94
|
+
send_url = options[:returns] ? (url + '?returns=true') : url
|
57
95
|
|
58
96
|
GMoney::GFRequest.should_receive(:new).with(send_url, :headers => {"Authorization" => "GoogleLogin auth=toke"}).and_return(@gf_request)
|
59
97
|
|
60
98
|
GMoney::GFService.should_receive(:send_request).with(@gf_request).and_return(@gf_response)
|
61
99
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
100
|
+
if options[:eager]
|
101
|
+
feed_ids = get_feed_ids(@gf_response.body)
|
102
|
+
feed_ids.each do |feed_id|
|
103
|
+
GMoney::Transaction.should_receive(:find).with(feed_id.position_feed_id, options).any_number_of_times.and_return(@transactions)
|
104
|
+
end
|
105
|
+
end
|
67
106
|
|
68
|
-
GMoney::Position.
|
69
|
-
end
|
70
|
-
|
71
|
-
def get_transaction_urls(feed)
|
72
|
-
doc = REXML::Document.new(feed)
|
73
|
-
feed_links = []
|
74
|
-
|
75
|
-
doc.elements.each('feed/entry') do |parsed_entry|
|
76
|
-
feed_links << parsed_entry.elements['gd:feedLink'].attributes['href']
|
77
|
-
end
|
78
|
-
feed_links
|
107
|
+
GMoney::Position.find(id, options)
|
79
108
|
end
|
80
109
|
end
|
data/spec/spec_helper.rb
CHANGED
data/spec/string_spec.rb
CHANGED
@@ -37,4 +37,43 @@ describe String do
|
|
37
37
|
'$123.23'.is_numeric?.should be_false
|
38
38
|
'.'.is_numeric?.should be_false
|
39
39
|
end
|
40
|
+
|
41
|
+
it "should be able to parse a portfolio id from an portfolio feed url" do
|
42
|
+
"http://finance.google.com/finance/feeds/user@example.com/portfolios/9".portfolio_feed_id.should be_eql("9")
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should be able to parse a position id from an position feed feed url" do
|
46
|
+
"http://finance.google.com/finance/feeds/user@example.com/portfolios/9/positions/NASDAG:GOOG".position_feed_id.should be_eql("9/NASDAG:GOOG")
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should be able to parse a portfolio id out of a string" do
|
50
|
+
1.portfolio_id.should be_eql("1")
|
51
|
+
"1".portfolio_id.should be_eql("1")
|
52
|
+
lambda { "asdf".portfolio_id }.should raise_error(String::PortfolioParseError)
|
53
|
+
lambda { "123/NASDAQ:GOOG/2134/23".portfolio_id }.should raise_error(String::PortfolioParseError)
|
54
|
+
"124".portfolio_id.should be_eql("124")
|
55
|
+
"1/NASDAQ:GOOG".portfolio_id.should be_eql("1")
|
56
|
+
"123/NASDAQ:GOOG".portfolio_id.should be_eql("123")
|
57
|
+
"123/NASDAQ:GOOG/23".portfolio_id.should be_eql("123")
|
58
|
+
end
|
59
|
+
|
60
|
+
it "should be able to parse a position id out of a string" do
|
61
|
+
1.position_id.should be_eql("")
|
62
|
+
"1".position_id.should be_eql("")
|
63
|
+
lambda { "asdf".position_id }.should raise_error(String::PositionParseError)
|
64
|
+
lambda {"123/NASDAQ:GOOG/2134/23".position_id}.should raise_error(String::PositionParseError)
|
65
|
+
"1/NASDAQ:GOOG".position_id.should be_eql("NASDAQ:GOOG")
|
66
|
+
"123/NASDAQ:GOOG".position_id.should be_eql("NASDAQ:GOOG")
|
67
|
+
"123/NASDAQ:GOOG/23".position_id.should be_eql("NASDAQ:GOOG")
|
68
|
+
end
|
69
|
+
|
70
|
+
it "should be able to parse a transaction id out of a string" do
|
71
|
+
lambda { 1.transaction_id }.should raise_error(String::TransactionParseError)
|
72
|
+
lambda { "1".transaction_id }.should raise_error(String::TransactionParseError)
|
73
|
+
lambda { "asdf".transaction_id }.should raise_error(String::TransactionParseError)
|
74
|
+
"1/NASDAQ:GOOG".transaction_id.should be_eql("")
|
75
|
+
lambda {"123/NASDAQ:GOOG/asdf".transaction_id}.should raise_error(String::TransactionParseError)
|
76
|
+
lambda {"123/NASDAQ:GOOG/2134/23".transaction_id}.should raise_error(String::TransactionParseError)
|
77
|
+
"123/NASDAQ:GOOG/23".transaction_id.should be_eql("23")
|
78
|
+
end
|
40
79
|
end
|
data/spec/transaction_spec.rb
CHANGED
@@ -3,10 +3,12 @@ require File.join(File.dirname(__FILE__), '/spec_helper')
|
|
3
3
|
describe GMoney::Transaction do
|
4
4
|
before(:all) do
|
5
5
|
@goog_feed = File.read('spec/fixtures/transactions_feed_for_GOOG.xml')
|
6
|
+
@goog_feed_1 = File.read('spec/fixtures/transaction_feed_for_GOOG_1.xml')
|
6
7
|
end
|
7
8
|
|
8
9
|
before(:each) do
|
9
10
|
@url = 'https://finance.google.com/finance/feeds/default/portfolios/9/positions/NASDAQ:GOOG/transactions'
|
11
|
+
@transaction_id = '9/NASDAQ:GOOG'
|
10
12
|
|
11
13
|
@gf_request = GMoney::GFRequest.new(@url)
|
12
14
|
@gf_request.method = :get
|
@@ -17,7 +19,7 @@ describe GMoney::Transaction do
|
|
17
19
|
end
|
18
20
|
|
19
21
|
it "should return all Tranasactions when the status code is 200" do
|
20
|
-
transactions = transaction_helper(@
|
22
|
+
transactions = transaction_helper(@transaction_id)
|
21
23
|
|
22
24
|
transactions.size.should be_eql(4)
|
23
25
|
transactions[1].commission.should be_eql(12.75)
|
@@ -25,22 +27,31 @@ describe GMoney::Transaction do
|
|
25
27
|
end
|
26
28
|
|
27
29
|
it "should raise an error when the status code is not 200" do
|
30
|
+
@transaction_id += '/1'
|
28
31
|
@gf_response.status_code = 404
|
32
|
+
@gf_response.body = "No transaction exists with tid 1"
|
29
33
|
|
30
|
-
lambda { transaction_helper(@
|
34
|
+
lambda { transaction_helper(@transaction_id) }.should raise_error(GMoney::Transaction::TransactionRequestError, @gf_response.body)
|
31
35
|
end
|
32
36
|
|
33
|
-
|
34
|
-
|
35
|
-
=
|
37
|
+
it "should return a specific transactions if the user request a specific transaction" do
|
38
|
+
@transaction_id += '/2'
|
39
|
+
@gf_response.body = @goog_feed_1
|
40
|
+
transaction = transaction_helper(@transaction_id)
|
36
41
|
|
37
|
-
|
42
|
+
transaction.commission.should be_eql(50.0)
|
43
|
+
transaction.price.should be_eql(400.0)
|
44
|
+
end
|
45
|
+
|
46
|
+
def transaction_helper(id, options={})
|
38
47
|
GMoney::GFSession.should_receive(:auth_token).and_return('toke')
|
39
48
|
|
49
|
+
url = "#{GMoney::GF_PORTFOLIO_FEED_URL}/#{id.portfolio_id}/positions/#{id.position_id}/transactions/#{id.transaction_id}"
|
50
|
+
|
40
51
|
GMoney::GFRequest.should_receive(:new).with(url, :headers => {"Authorization" => "GoogleLogin auth=toke"}).and_return(@gf_request)
|
41
52
|
|
42
53
|
GMoney::GFService.should_receive(:send_request).with(@gf_request).and_return(@gf_response)
|
43
54
|
|
44
|
-
GMoney::Transaction.
|
55
|
+
GMoney::Transaction.find(id, options)
|
45
56
|
end
|
46
57
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gmoney
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Justin Spradlin
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-
|
12
|
+
date: 2009-10-01 00:00:00 -04:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
@@ -21,6 +21,7 @@ extensions: []
|
|
21
21
|
|
22
22
|
extra_rdoc_files:
|
23
23
|
- README.rdoc
|
24
|
+
- lib/extensions/fixnum.rb
|
24
25
|
- lib/extensions/string.rb
|
25
26
|
- lib/gmoney.rb
|
26
27
|
- lib/gmoney/authentication_request.rb
|
@@ -40,6 +41,7 @@ files:
|
|
40
41
|
- README.rdoc
|
41
42
|
- Rakefile
|
42
43
|
- gmoney.gemspec
|
44
|
+
- lib/extensions/fixnum.rb
|
43
45
|
- lib/extensions/string.rb
|
44
46
|
- lib/gmoney.rb
|
45
47
|
- lib/gmoney/authentication_request.rb
|
@@ -60,9 +62,11 @@ files:
|
|
60
62
|
- spec/fixtures/empty_portfolio_feed.xml
|
61
63
|
- spec/fixtures/portfolio_9_feed.xml
|
62
64
|
- spec/fixtures/portfolio_feed_with_returns.xml
|
65
|
+
- spec/fixtures/position_feed_for_9_GOOG.xml
|
63
66
|
- spec/fixtures/positions_feed_for_portfolio_14.xml
|
64
67
|
- spec/fixtures/positions_feed_for_portfolio_9.xml
|
65
68
|
- spec/fixtures/positions_feed_for_portfolio_9r.xml
|
69
|
+
- spec/fixtures/transaction_feed_for_GOOG_1.xml
|
66
70
|
- spec/fixtures/transactions_feed_for_GOOG.xml
|
67
71
|
- spec/gmoney_spec.rb
|
68
72
|
- spec/portfolio_feed_parser_spec.rb
|