RubyNation is just a week and a half away and in order to prepare myself I thought I'd brush up on some Ruby and Rails in the days preceding the conference. I don't use Ruby on a regular basis, but I'm a huge fan of dynamic languages so I'm really looking forward to attending the conference and finding out what's new in the Ruby community.
By day I'm a Java developer, but I've also spent a fair amount of time studying Groovy. Learning Groovy has been my "training wheels" approach to understanding dynamic languages. Studying Groovy has helped me grasp new (new to me at least) concepts like Metaprogramming and Domain Specific Languages all from the comfort of my Java environment.
As a simple exercise I thought it would be interesting to rewrite the code from my last blog entry in Ruby to see how it compares to the Groovy code I originally wrote. So without further ado let's check out the code:
In order to use the Google Finance API we need to first authenticate with Google by sending our Google Account username and password to Google's client login URL: https://www.google.com/accounts/ClientLogin.
def create_auth_token(username, password)
https = Net::HTTP.new('www.google.com', 443)
https.use_ssl = true
path = '/accounts/ClientLogin'
query_string = "Email=#{username}&Passwd=#{password}
&service=finance&source=company-groovyfinance-1.0"
https.verify_mode = OpenSSL::SSL::VERIFY_NONE # don't display warnings
response = https.post(path, query_string)
if response.code == '200'
return response.body[/Auth=(.*)/, 1] # The authorization token
end
return "Error"
end
In the code above we create an http object and give it the correct URI and path to Google's login service. We then send in a query string with our username, password, service, and source (application name). If everything succeeds Google returns an Authentication token which we will set in the headers in subsequent calls to Google's service.
This code isn't much different from the Groovy code in my previous entry, but I do like the way Ruby allows POST calls directly on the http object. This obsoletes the processRequest() method found in my Groovy code.
We can create a new portfolio in our Google Finance account by executing calls to the following method:
def create_portfolio(authorization_token, portfolio_name)
headers={}
headers["Authorization"] = "GoogleLogin auth=#{authorization_token}"
headers["Content-Type"] = "application/atom+xml"
http = Net::HTTP.new('finance.google.com')
path = "/finance/feeds/default/portfolios"
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'>#{portfolio_name}</title>
<gf:portfolioData currencyCode='USD'/>
</entry>"
response = http.post(path, atom_string, headers)
if response.code == '201'
doc = REXML::Document.new(response.body)
return doc.root.elements["id"].text[/portfolios\/(.*)/, 1] #portfolio id
end
return "Error"
end
This code is almost identical to the Groovy code I wrote last time but I do really like the way Ruby allows you to place header attributes into a Hash and send them directly with a call to http.post().
Just like the example above, placing an order (stock trade) means that we need to create an Atom feed with our transaction attributes (transaction type, number of shares, etc.) and send them to a Google resource end point. This end point in our case is the URL of the specific portfolio in which we wish to place our new asset.
def create_stock_transaction(authorization_token, ticker_symbol,
number_of_shares, transaction_type, portfolio_id)
headers={}
headers["Authorization"] = "GoogleLogin auth=#{authorization_token}"
headers["Content-Type"] = "application/atom+xml"
http = Net::HTTP.new('finance.google.com')
path = "/finance/feeds/default/portfolios/#{portfolio_id}/positions/" +
"#{CGI::escape(ticker_symbol)}/transactions"
transaction_time = Time.now.strftime("%Y-%m-%dT%H:%M:%S.000")
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'>
<gf:transactionData date='#{transaction_time}'
shares='#{number_of_shares}' type='#{transaction_type}' />
</entry>"
response = http.post(path, atom_string, headers)
if response.code == '201'
return response.body
end
return "Error"
end
Once again the Ruby code looks nearly identical to the Groovy code. We set the correct headers, generate a well formed Atom feed, and the post our data to Google. If all goes well, the transaction will be made and an Atom representation of the transaction will be returned to the client.
There are probably "Rubyer" ways to write the code samples listed above, but I did find it interesting that the code I wrote in Ruby was so similar to the code that I wrote in Groovy. I'm not sure whether or not the similarities are due to the subjective nature of having the same programmer write both samples or if it's due to the fact that the languages are so similar to begin with.
Either way, exploring a new language (albeit a similar language) did give me some additional perspective. I found myself bouncing back and forth between the Groovy and Ruby code comparing how similar tasks were performed in each language. This type of information is always useful when trying to determine which technology is the right tool to use for a specific job.
In addition, studying Ruby helped me write better Groovy code. In the create_auth_token() method in the Ruby sample a regular expression is used to parse the authentication token from the returned string. I was able to take this regular expression code and apply it to the same method (createAuthToken()) in my Groovy code to improve its readability.
Again... I am in no way endorsing any of the companies mentioned in this blog entry and all company references are for example purposes only.
15 Responses to “Putting Google Finance to REST with Ruby”
Hey, this is good stuff. Thank you.
By Scott Motte on Feb 17, 2009
Thanks Scott!
By Justin Spradlin on Feb 17, 2009
Fantastic article. The only one I found with perfect examples that worked immediately! :) Thank You!
By Oscar McSpeed on Apr 19, 2009
Hey, Just wondering how I would get this to work. I am pretty clueless about coding but would like to get this installed on my website. It uses cpanel and the applications Ruby Gems and Ruby on Rails are installed. What do I do?
Thanks every so much in advance
regards
By James D on Jul 30, 2009
Hi James,
I am in the process of creating a Ruby Gem for the google finance services. Be sure to keep track of my github account over the next month or so: http://github.com/jspradlin
By Justin Spradlin on Jul 31, 2009
Hi, Thats wonderful… Your stuff helped me a lot. I am stuck at creating portfolio, the method ‘create_portfolio(authorization_token, portfolio_name)’ is returning “error”. can you help.
thank you,
aashish
By aashish on Oct 24, 2009
Hey aashish,
What error are you getting?
If you are just trying to read items from your portfolio you can use the GMoney gem I created (http://github.com/jspradlin/gmoney)
Just gem install gmoney
I’m working on a version that allows you to create, update, and delete portfolios as we speak, but I don’t have an estimate as to when it will be done.
By Justin Spradlin on Oct 24, 2009
the ‘create_portfolio(authorization_token, portfolio_name)’ is returning “error”.
How do I solve it.
By aashish on Oct 30, 2009
in the ‘create_portfolio(authorization_token, portfolio_name)’ method
AT THE LINE
response = http.post(path, atom_string, headers)
I am not getting response.code=201 so it is returning error
How do I solve it.
By aashish on Oct 30, 2009
Hello Aashish,
Download the code using this link:
http://www.justinspradlin.com/downloads/rubyfinance.zip
Go to line 99, put in your Google username and password. If you do not have a google account you will need to create one (https://www.google.com/accounts/NewAccount)
Open a command line and go to the directory where you extracted the code and run:
ruby rubyfinance.rb
I just tried it and it worked perfectly for me.
If you are still having issues you can read the documentation from google:
http://code.google.com/apis/finance/developers_guide_protocol.html
By Justin Spradlin on Oct 31, 2009
Thanks, It worked for me too..
By aashish on Oct 31, 2009
do you know how to develop installed application using fxruby to build google finance interface.
By aashish on Oct 31, 2009
Thank, for me its working well …
By Thiyagarajan Veluchamy on Nov 20, 2009
Thanks Thiyagarajan.
I’ve also just finished a Beta version of a gem called GMoney. This gem wraps the google finance API and allows you to use simple Ruby code to interact with Google Finance. If you are interested you can check out the documentation at:
http://github.com/jspradlin/gmoney
By Justin Spradlin on Nov 20, 2009