jspradlin-gmoney 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
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