jspradlin-gmoney 0.0.2

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.
Files changed (43) hide show
  1. data/Manifest +42 -0
  2. data/README.rdoc +29 -0
  3. data/Rakefile +25 -0
  4. data/gmoney.gemspec +30 -0
  5. data/lib/extensions/string.rb +10 -0
  6. data/lib/gmoney.rb +39 -0
  7. data/lib/gmoney/authentication_request.rb +52 -0
  8. data/lib/gmoney/feed_parser.rb +37 -0
  9. data/lib/gmoney/gf_request.rb +15 -0
  10. data/lib/gmoney/gf_response.rb +5 -0
  11. data/lib/gmoney/gf_service.rb +46 -0
  12. data/lib/gmoney/gf_session.rb +18 -0
  13. data/lib/gmoney/portfolio.rb +34 -0
  14. data/lib/gmoney/portfolio_feed_parser.rb +7 -0
  15. data/lib/gmoney/position.rb +33 -0
  16. data/lib/gmoney/position_feed_parser.rb +7 -0
  17. data/lib/gmoney/transaction.rb +21 -0
  18. data/lib/gmoney/transaction_feed_parser.rb +9 -0
  19. data/spec/authentication_request_spec.rb +101 -0
  20. data/spec/fixtures/cacert.pem +67 -0
  21. data/spec/fixtures/default_portfolios_feed.xml +45 -0
  22. data/spec/fixtures/empty_portfolio_feed.xml +24 -0
  23. data/spec/fixtures/portfolio_9_feed.xml +25 -0
  24. data/spec/fixtures/portfolio_feed_with_returns.xml +69 -0
  25. data/spec/fixtures/positions_feed_for_portfolio_14.xml +13 -0
  26. data/spec/fixtures/positions_feed_for_portfolio_9.xml +63 -0
  27. data/spec/fixtures/positions_feed_for_portfolio_9r.xml +130 -0
  28. data/spec/fixtures/transactions_feed_for_GOOG.xml +78 -0
  29. data/spec/gmoney_spec.rb +9 -0
  30. data/spec/portfolio_feed_parser_spec.rb +41 -0
  31. data/spec/portfolio_spec.rb +96 -0
  32. data/spec/position_feed_parser_spec.rb +39 -0
  33. data/spec/position_spec.rb +80 -0
  34. data/spec/request_spec.rb +29 -0
  35. data/spec/response_spec.rb +18 -0
  36. data/spec/service_spec.rb +127 -0
  37. data/spec/session_spec.rb +32 -0
  38. data/spec/spec.opts +2 -0
  39. data/spec/spec_helper.rb +1 -0
  40. data/spec/string_spec.rb +40 -0
  41. data/spec/transaction_feed_parser_spec.rb +29 -0
  42. data/spec/transaction_spec.rb +46 -0
  43. metadata +114 -0
@@ -0,0 +1,130 @@
1
+ <?xml version='1.0' encoding='utf-8'?>
2
+ <feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' 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</id>
4
+ <updated>2009-09-12T16:20:13.000Z</updated>
5
+ <category scheme='http://schemas.google.com/g/2005#kind' term='http://schemas.google.com/finance/2007#position' />
6
+ <title type='text'>GMoney Test</title>
7
+ <link rel='alternate' type='text/html' href='http://finance.google.com/finance/portfolio?action=view&amp;pid=9' />
8
+ <link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://finance.google.com/finance/feeds/default/portfolios/9/positions' />
9
+ <link rel='self' type='application/atom+xml' href='http://finance.google.com/finance/feeds/default/portfolios/9/positions?returns=true' />
10
+ <openSearch:totalResults>5</openSearch:totalResults>
11
+ <openSearch:startIndex>1</openSearch:startIndex>
12
+ <openSearch:itemsPerPage>5</openSearch:itemsPerPage>
13
+ <entry>
14
+ <id>http://finance.google.com/finance/feeds/user@example.com/portfolios/9/positions/NASDAQ:JAVA</id>
15
+ <updated>2009-09-12T16:20:13.000Z</updated>
16
+ <category scheme='http://schemas.google.com/g/2005#kind' term='http://schemas.google.com/finance/2007#position' />
17
+ <title type='text'>Sun Microsystems, Inc.</title>
18
+ <link rel='self' type='application/atom+xml' href='http://finance.google.com/finance/feeds/default/portfolios/9/positions/NASDAQ%3AJAVA' />
19
+ <gd:feedLink href='http://finance.google.com/finance/feeds/user@example.com/portfolios/9/positions/NASDAQ:JAVA/transactions' />
20
+ <gf:positionData gainPercentage='0.1182926829' return1w='-0.01967213115' return1y='0.09390243902' return3m='0.09390243902' return3y='0.09390243902' return4w='0.09390243902' return5y='0.09390243902' returnOverall='0.09390243902'
21
+ returnYTD='0.09390243902' shares='75.0'>
22
+ <gf:costBasis>
23
+ <gd:money amount='615.0' currencyCode='USD' />
24
+ </gf:costBasis>
25
+ <gf:daysGain>
26
+ <gd:money amount='0.0' currencyCode='USD' />
27
+ </gf:daysGain>
28
+ <gf:gain>
29
+ <gd:money amount='72.75' currencyCode='USD' />
30
+ </gf:gain>
31
+ <gf:marketValue>
32
+ <gd:money amount='687.75' currencyCode='USD' />
33
+ </gf:marketValue>
34
+ </gf:positionData>
35
+ <gf:symbol exchange='NASDAQ' fullName='Sun Microsystems, Inc.' symbol='JAVA' />
36
+ </entry>
37
+ <entry>
38
+ <id>http://finance.google.com/finance/feeds/user@example.com/portfolios/9/positions/NASDAQ:GOOG</id>
39
+ <updated>2009-09-12T16:20:13.000Z</updated>
40
+ <category scheme='http://schemas.google.com/g/2005#kind' term='http://schemas.google.com/finance/2007#position' />
41
+ <title type='text'>Google Inc.</title>
42
+ <link rel='self' type='application/atom+xml' href='http://finance.google.com/finance/feeds/default/portfolios/9/positions/NASDAQ%3AGOOG' />
43
+ <gd:feedLink href='http://finance.google.com/finance/feeds/user@example.com/portfolios/9/positions/NASDAQ:GOOG/transactions' />
44
+ <gf:positionData gainPercentage='0.1788764045' return1w='-0.002048558422' return1y='0.1494506866' return3m='0.1494506866' return3y='0.1494506866' return4w='0.1494506866' return5y='0.1494506866' returnOverall='0.1494506866' returnYTD='0.1494506866'
45
+ shares='75.0'>
46
+ <gf:costBasis>
47
+ <gd:money amount='30037.5' currencyCode='USD' />
48
+ </gf:costBasis>
49
+ <gf:daysGain>
50
+ <gd:money amount='90.0009' currencyCode='USD' />
51
+ </gf:daysGain>
52
+ <gf:gain>
53
+ <gd:money amount='5373.0' currencyCode='USD' />
54
+ </gf:gain>
55
+ <gf:marketValue>
56
+ <gd:money amount='35410.5' currencyCode='USD' />
57
+ </gf:marketValue>
58
+ </gf:positionData>
59
+ <gf:symbol exchange='NASDAQ' fullName='Google Inc.' symbol='GOOG' />
60
+ </entry>
61
+ <entry>
62
+ <id>http://finance.google.com/finance/feeds/user@example.com/portfolios/9/positions/NASDAQ:AAPL</id>
63
+ <updated>2009-09-12T16:20:13.000Z</updated>
64
+ <category scheme='http://schemas.google.com/g/2005#kind' term='http://schemas.google.com/finance/2007#position' />
65
+ <title type='text'>Apple Inc.</title>
66
+ <link rel='self' type='application/atom+xml' href='http://finance.google.com/finance/feeds/default/portfolios/9/positions/NASDAQ%3AAAPL' />
67
+ <gd:feedLink href='http://finance.google.com/finance/feeds/user@example.com/portfolios/9/positions/NASDAQ:AAPL/transactions' />
68
+ <gf:positionData gainPercentage='0.7216' return1w='0.01086254477' return1y='0.7216' return3m='0.7216' return3y='0.7216' return4w='0.7216' return5y='0.7216' returnOverall='0.7216' returnYTD='0.7216' shares='100.0'>
69
+ <gf:costBasis>
70
+ <gd:money amount='10000.0' currencyCode='USD' />
71
+ </gf:costBasis>
72
+ <gf:daysGain>
73
+ <gd:money amount='-39.9994' currencyCode='USD' />
74
+ </gf:daysGain>
75
+ <gf:gain>
76
+ <gd:money amount='7216.0' currencyCode='USD' />
77
+ </gf:gain>
78
+ <gf:marketValue>
79
+ <gd:money amount='17216.0' currencyCode='USD' />
80
+ </gf:marketValue>
81
+ </gf:positionData>
82
+ <gf:symbol exchange='NASDAQ' fullName='Apple Inc.' symbol='AAPL' />
83
+ </entry>
84
+ <entry>
85
+ <id>http://finance.google.com/finance/feeds/user@example.com/portfolios/9/positions/NASDAQ:ORCL</id>
86
+ <updated>2009-09-12T16:20:13.000Z</updated>
87
+ <category scheme='http://schemas.google.com/g/2005#kind' term='http://schemas.google.com/finance/2007#position' />
88
+ <title type='text'>Oracle Corporation</title>
89
+ <link rel='self' type='application/atom+xml' href='http://finance.google.com/finance/feeds/default/portfolios/9/positions/NASDAQ%3AORCL' />
90
+ <gd:feedLink href='http://finance.google.com/finance/feeds/user@example.com/portfolios/9/positions/NASDAQ:ORCL/transactions' />
91
+ <gf:positionData gainPercentage='0.143' return1w='0.04050978607' return1y='0.143' return3m='0.143' return3y='0.143' return4w='0.143' return5y='0.143' returnOverall='0.143' returnYTD='0.143' shares='75.0'>
92
+ <gf:costBasis>
93
+ <gd:money amount='1500.0' currencyCode='USD' />
94
+ </gf:costBasis>
95
+ <gf:daysGain>
96
+ <gd:money amount='7.5' currencyCode='USD' />
97
+ </gf:daysGain>
98
+ <gf:gain>
99
+ <gd:money amount='214.5' currencyCode='USD' />
100
+ </gf:gain>
101
+ <gf:marketValue>
102
+ <gd:money amount='1714.5' currencyCode='USD' />
103
+ </gf:marketValue>
104
+ </gf:positionData>
105
+ <gf:symbol exchange='NASDAQ' fullName='Oracle Corporation' symbol='ORCL' />
106
+ </entry>
107
+ <entry>
108
+ <id>http://finance.google.com/finance/feeds/user@example.com/portfolios/9/positions/NYSE:IBM</id>
109
+ <updated>2009-09-12T16:20:13.000Z</updated>
110
+ <category scheme='http://schemas.google.com/g/2005#kind' term='http://schemas.google.com/finance/2007#position' />
111
+ <title type='text'>International Business Machines Corp.</title>
112
+ <link rel='self' type='application/atom+xml' href='http://finance.google.com/finance/feeds/default/portfolios/9/positions/NYSE%3AIBM' />
113
+ <gd:feedLink href='http://finance.google.com/finance/feeds/user@example.com/portfolios/9/positions/NYSE:IBM/transactions' />
114
+ <gf:positionData gainPercentage='0.1805' return1w='-0.07894176741' return1y='0.081875' return3m='0.081875' return3y='0.081875' return4w='0.081875' return5y='0.081875' returnOverall='0.081875' returnYTD='0.081875' shares='75.0'>
115
+ <gf:costBasis>
116
+ <gd:money amount='7500.0' currencyCode='USD' />
117
+ </gf:costBasis>
118
+ <gf:daysGain>
119
+ <gd:money amount='28.500375' currencyCode='USD' />
120
+ </gf:daysGain>
121
+ <gf:gain>
122
+ <gd:money amount='1353.75' currencyCode='USD' />
123
+ </gf:gain>
124
+ <gf:marketValue>
125
+ <gd:money amount='8853.75' currencyCode='USD' />
126
+ </gf:marketValue>
127
+ </gf:positionData>
128
+ <gf:symbol exchange='NYSE' fullName='International Business Machines Corp.' symbol='IBM' />
129
+ </entry>
130
+ </feed>
@@ -0,0 +1,78 @@
1
+ <?xml version='1.0' encoding='utf-8'?>
2
+ <feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' 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</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'>Google Inc.</title>
7
+ <link rel='alternate' type='text/html' href='http://finance.google.com/finance/portfolio?action=viewt&amp;pid=9' />
8
+ <link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://finance.google.com/finance/feeds/default/portfolios/9/positions/NASDAQ%3AGOOG/transactions' />
9
+ <link rel='http://schemas.google.com/g/2005#post' type='application/atom+xml' href='http://finance.google.com/finance/feeds/default/portfolios/9/positions/NASDAQ%3AGOOG/transactions' />
10
+ <link rel='self' type='application/atom+xml' href='http://finance.google.com/finance/feeds/default/portfolios/9/positions/NASDAQ%3AGOOG/transactions' />
11
+ <openSearch:totalResults>4</openSearch:totalResults>
12
+ <openSearch:startIndex>1</openSearch:startIndex>
13
+ <openSearch:itemsPerPage>4</openSearch:itemsPerPage>
14
+ <entry>
15
+ <id>http://finance.google.com/finance/feeds/user@example.com/portfolios/9/positions/NASDAQ:GOOG/transactions/2</id>
16
+ <updated>2009-09-12T18:22:44.000Z</updated>
17
+ <category scheme='http://schemas.google.com/g/2005#kind' term='http://schemas.google.com/finance/2007#transaction' />
18
+ <title type='text'>2</title>
19
+ <link rel='self' type='application/atom+xml' href='http://finance.google.com/finance/feeds/default/portfolios/9/positions/NASDAQ%3AGOOG/transactions/2' />
20
+ <link rel='edit' type='application/atom+xml' href='http://finance.google.com/finance/feeds/default/portfolios/9/positions/NASDAQ%3AGOOG/transactions/2' />
21
+ <gf:transactionData date='2009-09-01T00:00:00.000' shares='100.0' type='Buy'>
22
+ <gf:commission>
23
+ <gd:money amount='50.0' currencyCode='USD' />
24
+ </gf:commission>
25
+ <gf:price>
26
+ <gd:money amount='400.0' currencyCode='USD' />
27
+ </gf:price>
28
+ </gf:transactionData>
29
+ </entry>
30
+ <entry>
31
+ <id>http://finance.google.com/finance/feeds/user@example.com/portfolios/9/positions/NASDAQ:GOOG/transactions/10</id>
32
+ <updated>2009-09-12T18:22:44.000Z</updated>
33
+ <category scheme='http://schemas.google.com/g/2005#kind' term='http://schemas.google.com/finance/2007#transaction' />
34
+ <title type='text'>10</title>
35
+ <link rel='self' type='application/atom+xml' href='http://finance.google.com/finance/feeds/default/portfolios/9/positions/NASDAQ%3AGOOG/transactions/10' />
36
+ <link rel='edit' type='application/atom+xml' href='http://finance.google.com/finance/feeds/default/portfolios/9/positions/NASDAQ%3AGOOG/transactions/10' />
37
+ <gf:transactionData date='2009-09-07T00:00:00.000' notes='Buy some more Google.' shares='100.0' type='Buy'>
38
+ <gf:commission>
39
+ <gd:money amount='12.75' currencyCode='USD' />
40
+ </gf:commission>
41
+ <gf:price>
42
+ <gd:money amount='425.0' currencyCode='USD' />
43
+ </gf:price>
44
+ </gf:transactionData>
45
+ </entry>
46
+ <entry>
47
+ <id>http://finance.google.com/finance/feeds/user@example.com/portfolios/9/positions/NASDAQ:GOOG/transactions/11</id>
48
+ <updated>2009-09-12T18:22:44.000Z</updated>
49
+ <category scheme='http://schemas.google.com/g/2005#kind' term='http://schemas.google.com/finance/2007#transaction' />
50
+ <title type='text'>11</title>
51
+ <link rel='self' type='application/atom+xml' href='http://finance.google.com/finance/feeds/default/portfolios/9/positions/NASDAQ%3AGOOG/transactions/11' />
52
+ <link rel='edit' type='application/atom+xml' href='http://finance.google.com/finance/feeds/default/portfolios/9/positions/NASDAQ%3AGOOG/transactions/11' />
53
+ <gf:transactionData date='2009-09-08T00:00:00.000' shares='150.0' type='Buy'>
54
+ <gf:commission>
55
+ <gd:money amount='0.0' currencyCode='USD' />
56
+ </gf:commission>
57
+ <gf:price>
58
+ <gd:money amount='425.0' currencyCode='USD' />
59
+ </gf:price>
60
+ </gf:transactionData>
61
+ </entry>
62
+ <entry>
63
+ <id>http://finance.google.com/finance/feeds/user@example.com/portfolios/9/positions/NASDAQ:GOOG/transactions/6</id>
64
+ <updated>2009-09-12T18:22:44.000Z</updated>
65
+ <category scheme='http://schemas.google.com/g/2005#kind' term='http://schemas.google.com/finance/2007#transaction' />
66
+ <title type='text'>6</title>
67
+ <link rel='self' type='application/atom+xml' href='http://finance.google.com/finance/feeds/default/portfolios/9/positions/NASDAQ%3AGOOG/transactions/6' />
68
+ <link rel='edit' type='application/atom+xml' href='http://finance.google.com/finance/feeds/default/portfolios/9/positions/NASDAQ%3AGOOG/transactions/6' />
69
+ <gf:transactionData date='2009-09-11T00:00:00.000' shares='25.0' type='Sell'>
70
+ <gf:commission>
71
+ <gd:money amount='50.0' currencyCode='USD' />
72
+ </gf:commission>
73
+ <gf:price>
74
+ <gd:money amount='427.0' currencyCode='USD' />
75
+ </gf:price>
76
+ </gf:transactionData>
77
+ </entry>
78
+ </feed>
@@ -0,0 +1,9 @@
1
+ require File.join(File.dirname(__FILE__), '/spec_helper')
2
+
3
+ describe GMoney do
4
+
5
+ it "should not have a nil VERSION number" do
6
+ GMoney.version.should_not be_nil
7
+ end
8
+
9
+ end
@@ -0,0 +1,41 @@
1
+ require File.join(File.dirname(__FILE__), '/spec_helper')
2
+
3
+ describe GMoney::PortfolioFeedParser do
4
+ before(:all) do
5
+ feed = File.read('spec/fixtures/default_portfolios_feed.xml')
6
+ @portfolios = GMoney::PortfolioFeedParser.parse_portfolio_feed(feed)
7
+
8
+ empty_feed = File.read('spec/fixtures/empty_portfolio_feed.xml')
9
+ @default_portfolios = GMoney::PortfolioFeedParser.parse_portfolio_feed(empty_feed)
10
+
11
+ feed_with_returns = File.read('spec/fixtures/portfolio_feed_with_returns.xml')
12
+ @portfolios_with_returns = GMoney::PortfolioFeedParser.parse_portfolio_feed(feed_with_returns)
13
+ end
14
+
15
+ it "should create Portfolio objects out of portfolio feeds" do
16
+ @portfolios.each do |portfolio|
17
+ portfolio.should be_instance_of(GMoney::Portfolio)
18
+ end
19
+ end
20
+
21
+ it "should return an Array of Portfolio objects equal to the size of Portfolio entries" do
22
+ @portfolios.size.should be_eql(3)
23
+ end
24
+
25
+ it "should return a default portfolio if the user has not made any of her own" do
26
+ @default_portfolios.size.should be_eql(1)
27
+ @default_portfolios[0].title.should be_eql('My Portfolio')
28
+ @default_portfolios.size.should be_eql(1)
29
+ end
30
+
31
+ it "should create Portfolio objects with valid numeric data types for the returns" do
32
+ @portfolios_with_returns.each do |portfolio|
33
+ portfolio.public_methods(false).each do |pm|
34
+ if !(['id', 'feed_link', 'updated', 'title', 'currency_code', 'positions'].include? pm) && !(pm.include?('='))
35
+ return_val = portfolio.send(pm)
36
+ return_val.should be_instance_of(Float) if return_val
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,96 @@
1
+ require File.join(File.dirname(__FILE__), '/spec_helper')
2
+
3
+ describe GMoney::Portfolio do
4
+ before(:all) do
5
+ @default_feed = File.read('spec/fixtures/default_portfolios_feed.xml')
6
+ @feed_with_returns = File.read('spec/fixtures/portfolio_feed_with_returns.xml')
7
+ @empty_feed = File.read('spec/fixtures/empty_portfolio_feed.xml')
8
+ @portfolio_9_feed = File.read('spec/fixtures/portfolio_9_feed.xml')
9
+ end
10
+
11
+ before(:each) do
12
+ @url = 'https://finance.google.com/finance/feeds/default/portfolios'
13
+
14
+ @gf_request = GMoney::GFRequest.new(@url)
15
+ @gf_request.method = :get
16
+
17
+ @gf_response = GMoney::GFResponse.new
18
+ @gf_response.status_code = 200
19
+ @gf_response.body = @default_feed
20
+ @positions = []
21
+ end
22
+
23
+ it "should return all Portfolios when status_code is 200" do
24
+ @gf_response.body = @default_feed
25
+ portfolios = portfolio_helper(@url)
26
+
27
+ portfolios.size.should be_eql(3)
28
+ end
29
+
30
+ it "should return a portfolio with returns data is :with_returns == true" do
31
+ @url = 'https://finance.google.com/finance/feeds/default/portfolios?returns=true'
32
+ @gf_response.body = @feed_with_returns
33
+
34
+ portfolios = portfolio_helper(@url, {:with_returns => true})
35
+
36
+ portfolios.size.should be_eql(3)
37
+ portfolios[0].cost_basis.should be_eql(2500.00)
38
+ portfolios[0].gain_percentage.should be_eql(28.3636)
39
+ portfolios[0].return4w.should be_eql(-0.1670616114)
40
+
41
+ portfolios[2].cost_basis.should be_nil
42
+ end
43
+
44
+ it "should raise an error if the portfolio request does not return an OK status code" do
45
+ @gf_response.status_code = 404
46
+
47
+ lambda { portfolio_helper(@url) }.should raise_error(GMoney::Portfolio::PortfolioRequestError)
48
+ end
49
+
50
+ it "should return an Array with only the default 'My Portfolio' portfolio when a user does not define his own portfolios" do
51
+ @gf_response.body = @empty_feed
52
+ portfolios = portfolio_helper(@url)
53
+
54
+ portfolios.size.should be_eql(1)
55
+ portfolios[0].title.should be_eql('My Portfolio')
56
+ end
57
+
58
+ =begin TODO - create a method that retreives individual portfolios
59
+ it "should return a specific portfolio is the request a specific portfolio" do
60
+ @url += '/9'
61
+ @gf_request.url = @url
62
+
63
+ @gf_response.body = @portfolio_9_feed
64
+ portfolios = portfolio_helper(@url)
65
+
66
+ portfolios.size.should be_eql(1)
67
+ portfolios[0].title.should be_eql('GMoney Test')
68
+ end
69
+ =end
70
+
71
+ def portfolio_helper(url, options = {})
72
+ GMoney::GFSession.should_receive(:auth_token).and_return('toke')
73
+
74
+ GMoney::GFRequest.should_receive(:new).with(url, :headers => {"Authorization" => "GoogleLogin auth=toke"}).and_return(@gf_request)
75
+
76
+ GMoney::GFService.should_receive(:send_request).with(@gf_request).and_return(@gf_response)
77
+
78
+ position_urls = get_position_urls(@gf_response.body)
79
+
80
+ position_urls.each do |position_url|
81
+ GMoney::Position.should_receive(:find_by_url).with(position_url, {:with_returns => options[:with_returns]}).any_number_of_times.and_return(@positions)
82
+ end
83
+
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
95
+ end
96
+ end
@@ -0,0 +1,39 @@
1
+ require File.join(File.dirname(__FILE__), '/spec_helper')
2
+
3
+ describe GMoney::PositionFeedParser do
4
+ before(:all) do
5
+ feed = File.read('spec/fixtures/positions_feed_for_portfolio_9.xml')
6
+ @positions = GMoney::PositionFeedParser.parse_position_feed(feed)
7
+
8
+ empty_feed = File.read('spec/fixtures/positions_feed_for_portfolio_14.xml')
9
+ @empty_positions = GMoney::PositionFeedParser.parse_position_feed(empty_feed)
10
+
11
+ feed_with_returns = File.read('spec/fixtures/positions_feed_for_portfolio_9r.xml')
12
+ @positions_with_returns = GMoney::PositionFeedParser.parse_position_feed(feed_with_returns)
13
+ end
14
+
15
+ it "should create Position objects out of position feeds" do
16
+ @positions.each do |position|
17
+ position.should be_instance_of(GMoney::Position)
18
+ end
19
+ end
20
+
21
+ it "should return an Array of Position objects equal to the size of Position entries" do
22
+ @positions.size.should be_eql(5)
23
+ end
24
+
25
+ it "should return an empty position if the user has not made any of her own" do
26
+ @default_positions.should be_nil
27
+ end
28
+
29
+ it "should create Position objects with valid numeric data types for the returns" do
30
+ @positions_with_returns.each do |position|
31
+ position.public_methods(false).each do |pm|
32
+ if !(['id', 'feed_link', 'updated', 'title', 'exchange', 'symbol', 'full_name', 'transactions'].include? pm) && !(pm.include?('='))
33
+ return_val = position.send(pm)
34
+ return_val.should be_instance_of(Float) if return_val
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,80 @@
1
+ require File.join(File.dirname(__FILE__), '/spec_helper')
2
+
3
+ describe GMoney::Position do
4
+ before(:all) do
5
+ @default_feed = File.read('spec/fixtures/positions_feed_for_portfolio_9.xml')
6
+ @feed_with_returns = File.read('spec/fixtures/positions_feed_for_portfolio_9r.xml')
7
+ @empty_feed = File.read('spec/fixtures/positions_feed_for_portfolio_14.xml')
8
+ end
9
+
10
+ before(:each) do
11
+ @url = 'https://finance.google.com/finance/feeds/default/portfolios/9/positions'
12
+
13
+ @gf_request = GMoney::GFRequest.new(@url)
14
+ @gf_request.method = :get
15
+
16
+ @gf_response = GMoney::GFResponse.new
17
+ @gf_response.status_code = 200
18
+ @gf_response.body = @defaul_feed
19
+ @transactions = []
20
+ end
21
+
22
+ it "should return all Positions when status_code is 200" do
23
+ @gf_response.body = @default_feed
24
+ positions = position_helper(@url)
25
+
26
+ positions.size.should be_eql(5)
27
+ end
28
+
29
+ it "should return a position with returns data is :with_returns == true" do
30
+ @gf_response.body = @feed_with_returns
31
+
32
+ positions = position_helper(@url, {:with_returns => true})
33
+
34
+ positions.size.should be_eql(5)
35
+ positions[0].cost_basis.should be_eql(615.00)
36
+ positions[0].gain_percentage.should be_eql(0.1182926829)
37
+ positions[0].return4w.should be_eql(0.09390243902)
38
+
39
+ positions[4].days_gain.should be_eql(28.500375)
40
+ end
41
+
42
+ 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)
46
+ end
47
+
48
+ =begin TODO - create a method that retreives individual positions
49
+ it "should return a specific position is the request a specific position" do
50
+ end
51
+ =end
52
+
53
+ def position_helper(url, options = {})
54
+ GMoney::GFSession.should_receive(:auth_token).and_return('toke')
55
+
56
+ send_url = options[:with_returns] ? (url + '?returns=true') : url
57
+
58
+ GMoney::GFRequest.should_receive(:new).with(send_url, :headers => {"Authorization" => "GoogleLogin auth=toke"}).and_return(@gf_request)
59
+
60
+ GMoney::GFService.should_receive(:send_request).with(@gf_request).and_return(@gf_response)
61
+
62
+ transaction_urls = get_transaction_urls(@gf_response.body)
63
+
64
+ transaction_urls.each do |transaction_url|
65
+ GMoney::Transaction.should_receive(:find_by_url).with(transaction_url).any_number_of_times.and_return(@transactions)
66
+ end
67
+
68
+ GMoney::Position.find_by_url(url, options)
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
79
+ end
80
+ end