gmoney 0.0.2 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|