gmoney 0.4.3 → 0.4.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -2,11 +2,11 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = %q{gmoney}
5
- s.version = "0.4.3"
5
+ s.version = "0.4.4"
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{2010-03-02}
9
+ s.date = %q{2010-04-13}
10
10
  s.description = %q{A gem for interacting with the Google Finance API}
11
11
  s.email = %q{jspradlin@gmail.com}
12
12
  s.extra_rdoc_files = ["README.rdoc", "lib/extensions/fixnum.rb", "lib/extensions/nil_class.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"]
@@ -1,3 +1,5 @@
1
+ # = Fixnum
2
+ # GMoney Fixnum Extensions
1
3
  class Fixnum
2
4
  def portfolio_id
3
5
  self.to_s.portfolio_id
@@ -1,3 +1,5 @@
1
+ # = NilClass
2
+ # GMoney NilClass Extensions
1
3
  class NilClass
2
4
  def blank?
3
5
  respond_to?(:empty?) ? empty? : !self
@@ -1,7 +1,20 @@
1
+ # = String
2
+ # GMoney String Extensions
1
3
  class String
4
+ # = ParseError
5
+ # Incorrect id/url parsing
2
6
  class ParseError < StandardError; end
7
+
8
+ # = PortfolioParseError
9
+ # Incorrect portfolio id/url parsing
3
10
  class PortfolioParseError < ParseError; end
11
+
12
+ # = PositionParseError
13
+ # Incorrect position id/url parsing
4
14
  class PositionParseError < ParseError; end
15
+
16
+ # = TransactionParseError
17
+ # Incorrect transaction id/url parsing
5
18
  class TransactionParseError < ParseError; end
6
19
 
7
20
  @@portfolio_re = /\d+/
@@ -11,7 +24,6 @@ class String
11
24
  @@transaction_re = /\d+\/[a-zA-Z]+:[a-zA-Z]+\/\d+/
12
25
  @@transaction_re_in = /^\d+\/[a-zA-Z]+:[a-zA-Z]+\/\d+$/
13
26
 
14
-
15
27
  def camel_to_us
16
28
  add_us = gsub(/(.)([A-Z][a-z]+)/, '\1_\2')
17
29
  add_us.gsub(/([a-z0-9])([A-Z])/, '\1_\2').downcase
@@ -26,17 +38,17 @@ class String
26
38
  end
27
39
 
28
40
  def position_feed_id
29
- portfolio = self[self.rindex('portfolios/')+11..index('/positions')-1]
41
+ portfolio = finance_feed_id_helper('portfolios', 11)
30
42
  position = self[rindex('/')+1..-1]
31
43
  "#{portfolio}/#{position}"
32
44
  end
33
45
 
34
46
  def transaction_feed_id
35
- portfolio = self[self.rindex('portfolios/')+11..index('/positions')-1]
36
- position = self[self.rindex('positions/')+10..index('/transactions')-1]
47
+ portfolio = finance_feed_id_helper('portfolios', 11)
48
+ position = finance_feed_id_helper('positions', 10)
37
49
  transaction = self[rindex('/')+1..-1]
38
50
  "#{portfolio}/#{position}/#{transaction}"
39
- end
51
+ end
40
52
 
41
53
  def portfolio_id
42
54
  if self[@@transaction_re_in] || self[@@position_re_in] || self[@@portfolio_re_in]
@@ -70,5 +82,12 @@ class String
70
82
 
71
83
  def blank?
72
84
  respond_to?(:empty?) ? self.strip.empty? : !self
73
- end
85
+ end
86
+
87
+ private
88
+
89
+ def finance_feed_id_helper(finance_object, offset)
90
+ f_objs = finance_object == 'portfolios' ? ['portfolios/', '/positions'] : ['positions/', '/transactions']
91
+ self[self.rindex(f_objs[0])+offset..index(f_objs[1])-1]
92
+ end
74
93
  end
@@ -1,8 +1,7 @@
1
1
  # Justin Spradlin
2
- # www.fiascode.com
2
+ # www.justinspradlin.com
3
3
  # (C) 2010
4
4
 
5
-
6
5
  $:.unshift File.expand_path(File.dirname(__FILE__))
7
6
 
8
7
  require 'date'
@@ -28,7 +27,7 @@ require 'gmoney/transaction'
28
27
  require 'gmoney/transaction_feed_parser'
29
28
 
30
29
  module GMoney
31
- VERSION = '0.4.3'
30
+ VERSION = '0.4.4'
32
31
  GF_URL = "http://finance.google.com/finance"
33
32
  GF_FEED_URL = "#{GF_URL}/feeds/default"
34
33
  GF_PORTFOLIO_FEED_URL = "#{GF_FEED_URL}/portfolios"
@@ -40,8 +39,7 @@ module GMoney
40
39
  BUY = 'Buy'
41
40
  SELL = 'Sell'
42
41
  SELL_SHORT = 'Sell Short'
43
- BUY_TO_COVER = 'Buy to Cover'
44
-
42
+ BUY_TO_COVER = 'Buy to Cover'
45
43
 
46
44
  def self.version
47
45
  VERSION
@@ -1,5 +1,14 @@
1
1
  module GMoney
2
+ # = AuthenticationRequest
3
+ #
4
+ # Authenticates the user's account with Google
5
+ #
2
6
  class AuthenticationRequest
7
+
8
+ # = AuthError
9
+ #
10
+ # Thrown when there is a network or Authentication error.
11
+ #
3
12
  class AuthError < StandardError;end
4
13
 
5
14
  AUTH_URL = 'https://www.google.com/accounts/ClientLogin'
@@ -1,4 +1,9 @@
1
1
  module GMoney
2
+ # = FeedParser
3
+ #
4
+ # Parses Atom feeds that are returned from the Google Finance API
5
+ # and converts its data into Ruby objects.
6
+ #
2
7
  class FeedParser
3
8
  def self.parse_feed(feed, feed_class, options = {:feed_link => true, :symbol => false})
4
9
  doc = REXML::Document.new(feed)
@@ -1,4 +1,8 @@
1
1
  module GMoney
2
+ # = GFRequest
3
+ #
4
+ # Wraps Requests that are sent to Google via GMoney
5
+ #
2
6
  class GFRequest
3
7
  attr_accessor :url, :body, :method, :headers
4
8
 
@@ -11,6 +15,7 @@ module GMoney
11
15
  @method ||= :get
12
16
  @headers ||= {}
13
17
  @headers['GData-Version'] = GF_GOOGLE_DATA_VERSION
18
+ @headers['Authorization'] = "GoogleLogin auth=#{GFSession.auth_token}"
14
19
  end
15
20
  end
16
21
  end
@@ -1,4 +1,8 @@
1
1
  module GMoney
2
+ # = GFResponse
3
+ #
4
+ # Wraps Responses that are received from Google
5
+ #
2
6
  class GFResponse
3
7
  attr_accessor :status_code, :body, :headers
4
8
  end
@@ -3,6 +3,10 @@ require 'net/https'
3
3
  require 'uri'
4
4
 
5
5
  module GMoney
6
+ # = GFService
7
+ #
8
+ # Used to send and receive RESTful request/responses from Google
9
+ #
6
10
  class GFService
7
11
  def self.send_request(request)
8
12
  url = URI.parse(request.url)
@@ -1,4 +1,10 @@
1
1
  module GMoney
2
+
3
+ # = GFSession
4
+ #
5
+ # Holds the authenicated user's token which is need
6
+ # for each request sent to Google
7
+ #
2
8
  class GFSession
3
9
 
4
10
  def self.login(email, password, opts={})
@@ -14,5 +20,10 @@ module GMoney
14
20
  def self.email
15
21
  @email
16
22
  end
23
+
24
+ def self.logout
25
+ @email = nil
26
+ @auth_token = nil
27
+ end
17
28
  end
18
29
  end
@@ -1,7 +1,24 @@
1
1
  module GMoney
2
+ # = Portfolio
3
+ #
4
+ # A Google Finace API Portfolio holds a collection of Positions
5
+ # and their associated Transactions
6
+ #
7
+ # > portfolio = GMoney::Portfolio.new
8
+ # > portfolio.title = "My New Portfolio"
9
+ # > portfolio.save #returns portfolio object
10
+ #
2
11
  class Portfolio
3
- class PortfolioRequestError < StandardError;end
12
+ # = PortfolioRequestError
13
+ # Invalid request actions or identifiers
14
+ class PortfolioRequestError < StandardError;end
15
+
16
+ # = PortfolioDeleteError
17
+ # Invalid delete action or identifier
4
18
  class PortfolioDeleteError < StandardError;end
19
+
20
+ # = PortfolioSaveError
21
+ # Invalid save action or identifier
5
22
  class PortfolioSaveError < StandardError;end
6
23
 
7
24
  attr_accessor :title, :currency_code
@@ -51,7 +68,7 @@ module GMoney
51
68
  url += "?returns=true" if options[:returns]
52
69
  portfolios = []
53
70
 
54
- response = GFService.send_request(GFRequest.new(url, :headers => {"Authorization" => "GoogleLogin auth=#{GFSession.auth_token}"}))
71
+ response = GFService.send_request(GFRequest.new(url))
55
72
 
56
73
  if response.status_code == HTTPOK
57
74
  portfolios = PortfolioFeedParser.parse_portfolio_feed(response.body)
@@ -59,7 +76,7 @@ module GMoney
59
76
  raise PortfolioRequestError, response.body
60
77
  end
61
78
 
62
- portfolios.each { |p| p.instance_variable_set("@positions", p.positions(options))} if options[:eager]
79
+ portfolios.each { |port| port.instance_variable_set("@positions", port.positions(options))} if options[:eager]
63
80
 
64
81
  return portfolios[0] if portfolios.size == 1
65
82
 
@@ -77,7 +94,7 @@ module GMoney
77
94
 
78
95
  #Some firewalls block HTTP PUT messages. To get around this, you can include a
79
96
  #X-HTTP-Method-Override: PUT header in a POST request
80
- headers = {"Authorization" => "GoogleLogin auth=#{GFSession.auth_token}", "Content-Type" => "application/atom+xml"}
97
+ headers = {"Content-Type" => "application/atom+xml"}
81
98
  headers["X-HTTP-Method-Override"] = "PUT" if @id #if there is already an @id defined then we are updating a portfolio
82
99
 
83
100
  request = GFRequest.new(url, :method => :post, :body => atom_string, :headers => headers)
@@ -97,7 +114,7 @@ module GMoney
97
114
  #X-HTTP-Method-Override: DELETE header in a POST request
98
115
  def self.delete_portfolio(id)
99
116
  url = "#{GF_PORTFOLIO_FEED_URL}/#{id}"
100
- response = GFService.send_request(GFRequest.new(url, :method => :post, :headers => {"Authorization" => "GoogleLogin auth=#{GFSession.auth_token}", "X-HTTP-Method-Override" => "DELETE"}))
117
+ response = GFService.send_request(GFRequest.new(url, :method => :post, :headers => {"X-HTTP-Method-Override" => "DELETE"}))
101
118
  raise PortfolioDeleteError, response.body if response.status_code != HTTPOK
102
119
  end
103
120
 
@@ -1,4 +1,8 @@
1
1
  module GMoney
2
+ # = PortfolioFeedParser
3
+ #
4
+ # Parses Portfolio feeds returned from the Google Finance API
5
+ #
2
6
  class PortfolioFeedParser < FeedParser
3
7
  def self.parse_portfolio_feed(portfolio_feed)
4
8
  parse_feed(portfolio_feed, Portfolio)
@@ -1,6 +1,20 @@
1
1
  module GMoney
2
+ # = Position
3
+ #
4
+ # The Google Finace API allows users to view position information
5
+ # for the securities (i.e. Positions) in their portfolios.
6
+ # To read a position use the following code:
7
+ #
8
+ # > positions = GMoney::Position.find(9) #returns all of a user's positions within a given portfolio, i.e. Portfolio "9"
9
+ # > position = GMoney::Position.find("9/NASDAQ:GOOG") #returns a specific position within a given portfolio
10
+ #
2
11
  class Position
12
+ # = PositionRequestError
13
+ # Invalid request actions or identifiers
3
14
  class PositionRequestError < StandardError; end
15
+
16
+ # = PositionDeleteError
17
+ # Invalid delete actions or identifiers
4
18
  class PositionDeleteError < StandardError; end
5
19
 
6
20
  attr_reader :id, :updated, :title, :feed_link, :exchange, :symbol, :shares,
@@ -39,7 +53,7 @@ module GMoney
39
53
  positions = []
40
54
  url += "?returns=true" if options[:returns]
41
55
 
42
- response = GFService.send_request(GFRequest.new(url, :headers => {"Authorization" => "GoogleLogin auth=#{GFSession.auth_token}"}))
56
+ response = GFService.send_request(GFRequest.new(url))
43
57
 
44
58
  if response.status_code == HTTPOK
45
59
  positions = PositionFeedParser.parse_position_feed(response.body) if response.status_code == 200
@@ -1,4 +1,8 @@
1
1
  module GMoney
2
+ # = PositionFeedParser
3
+ #
4
+ # Parses Position feeds returned from the Google Finance API
5
+ #
2
6
  class PositionFeedParser < FeedParser
3
7
  def self.parse_position_feed(position_feed)
4
8
  parse_feed(position_feed, Position, {:feed_link => true, :symbol => true})
@@ -1,8 +1,31 @@
1
1
  module GMoney
2
+ # = Transaction
3
+ #
4
+ # The Google Finace API allows for the buying and selling
5
+ # of securities (i.e. Positions) through Transactions. To
6
+ # create a valid Transaction use the following code:
7
+ #
8
+ # > transaction.portfolio = 9 #Must be a valid portfolio id
9
+ # > transaction.ticker = 'nyse:c' #Must be a valid ticker symbol
10
+ # > transaction.type = GMoney::BUY #Must be one of the following: Buy, Sell, Sell Short, Buy to Cover
11
+ # > transaction.save #returns transaction object
12
+ #
2
13
  class Transaction
3
- class TransactionRequestError < StandardError; end
4
- class TransactionDeleteError < StandardError;end
14
+ # = TransactionRequestError
15
+ # Invalid request actions or identifiers
16
+ class TransactionRequestError < StandardError; end
17
+
18
+ # = TransactionDeleteError
19
+ # Invalid delete action or identifier
20
+ class TransactionDeleteError < StandardError;end
21
+
22
+ # = TransactionSaveError
23
+ # Invalid save action or identifier
5
24
  class TransactionSaveError < StandardError;end
25
+
26
+ # = TransactionIdError
27
+ # Don't allow users to modify the portfolio or position of
28
+ # a transaction that has already been created.
6
29
  class TransactionIdError < StandardError;end
7
30
 
8
31
  attr_reader :id, :updated, :title
@@ -28,18 +51,18 @@ module GMoney
28
51
  end
29
52
  end
30
53
 
31
- def portfolio=(p)
54
+ def portfolio=(port)
32
55
  raise TransactionIdError, "You can't modify the portfolio for a Transaction that already has an id" if @id
33
- @portfolio = p
56
+ @portfolio = port
34
57
  end
35
58
 
36
- def ticker=(t)
59
+ def ticker=(tick)
37
60
  raise TransactionIdError, "You can't modify the ticker for a Transaction that already has an id" if @id
38
- @ticker = t
61
+ @ticker = tick
39
62
  end
40
63
 
41
64
  def self.find(id, options={})
42
- find_by_url("#{GF_PORTFOLIO_FEED_URL}/#{id.portfolio_id}/positions/#{id.position_id}/transactions/#{id.transaction_id}", options)
65
+ find_by_url(transaction_url(id), options)
43
66
  end
44
67
 
45
68
  def save
@@ -58,7 +81,7 @@ module GMoney
58
81
  def self.find_by_url(url, options={})
59
82
  transactions = []
60
83
 
61
- response = GFService.send_request(GFRequest.new(url, :headers => {"Authorization" => "GoogleLogin auth=#{GFSession.auth_token}"}))
84
+ response = GFService.send_request(GFRequest.new(url))
62
85
 
63
86
  if response.status_code == HTTPOK
64
87
  transactions = TransactionFeedParser.parse_transaction_feed(response.body)
@@ -75,8 +98,7 @@ module GMoney
75
98
  #To overcome this problem the google doc say to use a post request with
76
99
  #the X-HTTP-Method-Override set to "DELETE"
77
100
  def self.delete_transaction(id)
78
- url = "#{GF_PORTFOLIO_FEED_URL}/#{id.portfolio_id}/positions/#{id.position_id}/transactions/#{id.transaction_id}"
79
- response = GFService.send_request(GFRequest.new(url, :method => :post, :headers => {"Authorization" => "GoogleLogin auth=#{GFSession.auth_token}", "X-HTTP-Method-Override" => "DELETE"}))
101
+ response = GFService.send_request(GFRequest.new(transaction_url(id), :method => :post, :headers => {"X-HTTP-Method-Override" => "DELETE"}))
80
102
  raise TransactionDeleteError, response.body if response.status_code != HTTPOK
81
103
  end
82
104
 
@@ -101,7 +123,7 @@ module GMoney
101
123
 
102
124
  #Some firewalls block HTTP PUT messages. To get around this, you can include a
103
125
  #X-HTTP-Method-Override: PUT header in a POST request
104
- headers = {"Authorization" => "GoogleLogin auth=#{GFSession.auth_token}", "Content-Type" => "application/atom+xml"}
126
+ headers = {"Content-Type" => "application/atom+xml"}
105
127
  headers["X-HTTP-Method-Override"] = "PUT" if @id #if there is already an @id defined then we are updating a transaction
106
128
 
107
129
  request = GFRequest.new(url, :method => :post, :body => atom_string, :headers => headers)
@@ -125,7 +147,11 @@ module GMoney
125
147
  [BUY, SELL, SELL_SHORT, BUY_TO_COVER].include?(@type)
126
148
  end
127
149
 
128
- private :save_transaction, :is_valid_transaction?, :is_valid_transaction_type?
150
+ def transaction_url(id)
151
+ "#{GF_PORTFOLIO_FEED_URL}/#{id.portfolio_id}/positions/#{id.position_id}/transactions/#{id.transaction_id}"
152
+ end
153
+
154
+ private :save_transaction, :is_valid_transaction?, :is_valid_transaction_type?, :transaction_url
129
155
  private_class_method :find_by_url, :delete_transaction
130
156
  end
131
157
  end
@@ -1,4 +1,8 @@
1
1
  module GMoney
2
+ # = TransactionFeedParser
3
+ #
4
+ # Parses Transaction feeds returned from the Google Finance API
5
+ #
2
6
  class TransactionFeedParser < FeedParser
3
7
  def self.parse_transaction_feed(transaction_feed)
4
8
  parse_feed(transaction_feed, Transaction, {:feed_link => false})
@@ -14,7 +14,7 @@ describe GMoney::Portfolio do
14
14
  @url = "#{GMoney::GF_URL}/feeds/default/portfolios"
15
15
 
16
16
  @gf_request = GMoney::GFRequest.new(@url)
17
- @gf_request.method = :get
17
+ @gf_request.method = :get
18
18
 
19
19
  @gf_response = GMoney::GFResponse.new
20
20
  @gf_response.status_code = 200
@@ -198,10 +198,8 @@ describe GMoney::Portfolio do
198
198
  lambda { portfolio.save }.should raise_error(GMoney::Portfolio::PortfolioSaveError, 'A Portfolio with this name already exists.')
199
199
  end
200
200
 
201
- def portfolio_helper(url, id = nil, options = {})
202
- GMoney::GFSession.should_receive(:auth_token).and_return('toke')
203
-
204
- GMoney::GFRequest.should_receive(:new).with(url, :headers => {"Authorization" => "GoogleLogin auth=toke"}).and_return(@gf_request)
201
+ def portfolio_helper(url, id = nil, options = {})
202
+ GMoney::GFRequest.should_receive(:new).with(url).and_return(@gf_request)
205
203
 
206
204
  GMoney::GFService.should_receive(:send_request).with(@gf_request).and_return(@gf_response)
207
205
 
@@ -215,10 +213,8 @@ describe GMoney::Portfolio do
215
213
  portfolios = id ? GMoney::Portfolio.find(id, options) : GMoney::Portfolio.all(options)
216
214
  end
217
215
 
218
- def portfolio_delete_helper(url)
219
- GMoney::GFSession.should_receive(:auth_token).and_return('toke')
220
-
221
- GMoney::GFRequest.should_receive(:new).with(url, :method => :post, :headers => {"Authorization" => "GoogleLogin auth=toke", "X-HTTP-Method-Override" => "DELETE"}).and_return(@gf_request)
216
+ def portfolio_delete_helper(url)
217
+ GMoney::GFRequest.should_receive(:new).with(url, :method => :post, :headers => {"X-HTTP-Method-Override" => "DELETE"}).and_return(@gf_request)
222
218
 
223
219
  GMoney::GFService.should_receive(:send_request).with(@gf_request).and_return(@gf_response)
224
220
  end
@@ -229,11 +225,9 @@ describe GMoney::Portfolio do
229
225
 
230
226
  atom_string = "<?xml version='1.0'?><entry xmlns='http://www.w3.org/2005/Atom' xmlns:gf='http://schemas.google.com/finance/2007' xmlns:gd='http://schemas.google.com/g/2005'><title type='text'>#{title}</title> <gf:portfolioData currencyCode='#{currency_code}'/></entry>"
231
227
 
232
- url = portfolio.id ? portfolio.id : GMoney::GF_PORTFOLIO_FEED_URL
233
-
234
- GMoney::GFSession.should_receive(:auth_token).and_return('toke')
228
+ url = portfolio.id ? portfolio.id : GMoney::GF_PORTFOLIO_FEED_URL
235
229
 
236
- headers = {"Authorization" => "GoogleLogin auth=toke", "Content-Type" => "application/atom+xml"}
230
+ headers = {"Content-Type" => "application/atom+xml"}
237
231
  headers["X-HTTP-Method-Override"] = "PUT" if portfolio.id
238
232
 
239
233
  GMoney::GFRequest.should_receive(:new).with(url, :method => :post, :body => atom_string, :headers => headers).and_return(@gf_request)
@@ -144,12 +144,10 @@ describe GMoney::Position do
144
144
  end
145
145
 
146
146
  def position_helper(id, options = {})
147
- GMoney::GFSession.should_receive(:auth_token).and_return('toke')
148
-
149
147
  url = "#{GMoney::GF_PORTFOLIO_FEED_URL}/#{id.portfolio_id}/positions/#{id.position_id}"
150
148
  send_url = options[:returns] ? (url + '?returns=true') : url
151
149
 
152
- GMoney::GFRequest.should_receive(:new).with(send_url, :headers => {"Authorization" => "GoogleLogin auth=toke"}).and_return(@gf_request)
150
+ GMoney::GFRequest.should_receive(:new).with(send_url).and_return(@gf_request)
153
151
 
154
152
  GMoney::GFService.should_receive(:send_request).with(@gf_request).and_return(@gf_response)
155
153
 
@@ -18,7 +18,7 @@ describe GMoney::GFRequest do
18
18
 
19
19
  it "should be able to take header parameters as a Hash" do
20
20
  gfrequest_with_header_hash = GMoney::GFRequest.new('http://someurl.com', {:headers => {:header1 => 'some header'}})
21
- gfrequest_with_header_hash.headers.should == {:header1 => 'some header', "GData-Version" => 2}
21
+ gfrequest_with_header_hash.headers.should == {:header1 => 'some header', "GData-Version" => 2, "Authorization" => "GoogleLogin auth="}
22
22
  end
23
23
 
24
24
  it "should not accept random options" do
@@ -123,6 +123,7 @@ describe GMoney::GFService do
123
123
  def set_header_expectations(req)
124
124
  req.should_receive(:[]=).with(:h1, "header 1").any_number_of_times
125
125
  req.should_receive(:[]=).with(:h2, "header 2").any_number_of_times
126
- req.should_receive(:[]=).with("GData-Version", 2).any_number_of_times
126
+ req.should_receive(:[]=).with("GData-Version", 2).any_number_of_times
127
+ req.should_receive(:[]=).with("Authorization", "GoogleLogin auth=").any_number_of_times
127
128
  end
128
129
  end
@@ -29,4 +29,23 @@ describe GMoney::GFSession do
29
29
  GMoney::GFSession.login('email@example.com', 'password')
30
30
  GMoney::GFSession.email.should be_eql('email@example.com')
31
31
  end
32
+
33
+ it "should allow users to logout" do
34
+ @auth_request.should_receive(:auth_token).with({}).and_return('toke')
35
+
36
+ GMoney::AuthenticationRequest.should_receive(:new).with('email@example.com', 'password').once.and_return(@auth_request)
37
+ GMoney::GFSession.login('email@example.com', 'password')
38
+
39
+ GMoney::GFSession.email.should be_eql('email@example.com')
40
+ GMoney::GFSession.auth_token.should be_eql('toke')
41
+
42
+ GMoney::GFSession.logout
43
+
44
+ GMoney::GFSession.email.should be_nil
45
+ GMoney::GFSession.auth_token.should be_nil
46
+ end
47
+
48
+ after(:all) do
49
+ GMoney::GFSession.logout
50
+ end
32
51
  end
@@ -268,13 +268,21 @@ describe GMoney::Transaction do
268
268
  lambda { transaction.save }.should raise_error(GMoney::Transaction::TransactionSaveError, "Some of the values submitted are not valid and have been ignored.")
269
269
 
270
270
  end
271
+
272
+ it "should create a url out of a Portfolio id" do
273
+ #Set methods to public visibility for testing
274
+ GMoney::Transaction.send(:public, *GMoney::Transaction.private_instance_methods)
275
+ trans = GMoney::Transaction.new
276
+ transaction_string = trans.transaction_url("123/NASDAQ:GOOG/23")
277
+ transaction_string.should be_eql("#{GMoney::GF_PORTFOLIO_FEED_URL}/123/positions/NASDAQ:GOOG/transactions/23")
278
+ end
271
279
 
272
280
  def transaction_helper(id, options={})
273
- GMoney::GFSession.should_receive(:auth_token).and_return('toke')
274
-
275
281
  url = "#{GMoney::GF_PORTFOLIO_FEED_URL}/#{id.portfolio_id}/positions/#{id.position_id}/transactions/#{id.transaction_id}"
276
282
 
277
- GMoney::GFRequest.should_receive(:new).with(url, :headers => {"Authorization" => "GoogleLogin auth=toke"}).and_return(@gf_request)
283
+ GMoney::Transaction.should_receive(:transaction_url).with(id).and_return(url)
284
+
285
+ GMoney::GFRequest.should_receive(:new).with(url).and_return(@gf_request)
278
286
 
279
287
  GMoney::GFService.should_receive(:send_request).with(@gf_request).and_return(@gf_response)
280
288
 
@@ -282,9 +290,9 @@ describe GMoney::Transaction do
282
290
  end
283
291
 
284
292
  def transaction_delete_helper(url)
285
- GMoney::GFSession.should_receive(:auth_token).and_return('toke')
293
+ GMoney::Transaction.should_receive(:transaction_url).and_return(url)
286
294
 
287
- GMoney::GFRequest.should_receive(:new).with(url, :method => :post, :headers => {"Authorization" => "GoogleLogin auth=toke", "X-HTTP-Method-Override" => "DELETE"}).and_return(@gf_request)
295
+ GMoney::GFRequest.should_receive(:new).with(url, :method => :post, :headers => {"X-HTTP-Method-Override" => "DELETE"}).and_return(@gf_request)
288
296
 
289
297
  GMoney::GFService.should_receive(:send_request).with(@gf_request).and_return(@gf_response)
290
298
  end
@@ -304,9 +312,7 @@ describe GMoney::Transaction do
304
312
 
305
313
  url = transaction.id ? transaction.id : "#{GMoney::GF_PORTFOLIO_FEED_URL}/#{transaction.portfolio}/positions/#{transaction.ticker}/transactions"
306
314
 
307
- GMoney::GFSession.should_receive(:auth_token).and_return('toke')
308
-
309
- headers = {"Authorization" => "GoogleLogin auth=toke", "Content-Type" => "application/atom+xml"}
315
+ headers = {"Content-Type" => "application/atom+xml"}
310
316
  headers["X-HTTP-Method-Override"] = "PUT" if transaction.id
311
317
 
312
318
  GMoney::GFRequest.should_receive(:new).with(url, :method => :post, :body => atom_string, :headers => headers).and_return(@gf_request)
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.4.3
4
+ version: 0.4.4
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: 2010-03-02 00:00:00 -05:00
12
+ date: 2010-04-13 00:00:00 -04:00
13
13
  default_executable:
14
14
  dependencies: []
15
15