Making Twitter mobile client with Ruby using Rhodes & Rhosync (Part 1)
Once you play with sample applications provided from rhomobile, now you might want to make something on your own.
I’ve seen lots of desktop client app tutorial with “Creating Sample Twitter App” examples, so I also tried something similar.
I will try to explain my code examples step by step including error messages and mistakes I made, so that you can not only experience what it is like to develop mobile app using Rhodes/Rhosync, but also debug similar problems when you make your own application.
In this tutorial, I will start from minimum functionality to display Twitter’s public timeline on iPhone simulator. I will then implement authentication and basic Read/Create actions , so that you can show your friends’s tweet, as well as post your own tweet from the iPhone simulator.
All the codes will be tested against 0.3.1 tags for both rhosync and rhodes.
Before I start, special thanks to Rhomobile support team for answering so many questions at google group.
What is Twitter API?
Twitter is one of the most post popular Ruby On Rails based web app. It is a micro blogging service where you can post your status in small text(140 chars).
Following Ruby On Rails convention, they have very clean (
REST based APIs). If you have never tried their API, click http://twitter.com/statuses/public_timeline.xml. You will see list of 20 public tweets in xml format like below.
<status>
<created_at>Sat Feb 07 23:49:04 +0000 2009</created_at>
<id>1187577382</id>
<text>
@ceetee dude! How's it like out there! Been there before?
</text>
<source>
<a href="http://www.atebits.com/software/tweetie/">Tweetie</a>
</source>
<truncated>false</truncated>
<in_reply_to_status_id>1187570116</in_reply_to_status_id>
<in_reply_to_user_id>8620122</in_reply_to_user_id>
<favorited>false</favorited>
<user>
<id>11686132</id>
<name>bombayaddict</name>
<screen_name>b50</screen_name>
<description>Bombay's Addict</description>
<location>Bombay. Always. </location>
<profile_image_url>
</profile_image_url>
<url>http://www.bombayaddict.com</url>
<protected>false</protected>
<followers_count>462</followers_count>
</user>
</status>
If you change file extension from .xml to .json or .rss, they will show in each format. Unlike your private tweets which I cover later, accessing public tweets does not require authentication.
There are a few Ruby Libraries to handle their API, but the APIs are so easy that you can do most of what you want to do by just using basic Ruby network library, such as net/http.
Using Rhosync
Rhosync will let you define common CRUD(Create, Read, Update, Delete) interfaces for mobile device via Rhodes.
Rhosync is built as a pure Ruby On Rails based application, so you should not have any problems if you know how to run and develop using Ruby On Rails.
Registering new source at Rhosync web form.
After you install Rhosync following the instruction, let’s startup the server, register your account, login to the server, and create an application called “Twitter”. Once application is created, click “Add Source” and create an source called “Twitter Public Timeline”
Add “http://twitter.com” as url, but you can leave login and password for now, as Public Timeline does not require authentication.
To setup adapter, you can either generate the adapter using “rhogen” command, or type source code directly from the web form. I choose generating adapter myself for better debugging purpose. If your adapter code has some bugs, any exception will point the line number which contains the bug. If you type code in the form, your code is kept at database and get evaluated dynamically when required, but exception only says your eval failed, which is harder to debug.
If you go for generating adapter, just type “PublicTimeline” at “Source Adapter Class”
rhogen
you need to create an adapter to talk to Twitter. Rhosync has some built in code generator, so creating the skeleton is very easy.
$ rhogen source PublicTimeline
Generating with source generator:
[ADDED] lib/public_timeline.rb
This creates long list of code defining login, query, sync, create, update, delete, and logoff.
For this very first application, all you need to worry about are “query” and “sync” methods.
query method
Here is my mimimalist code to fetch query from twitter.
def query
uri = URI.parse(@source.url)
res = Net::HTTP.start(uri.host, uri.port) {|http|
http.get("/statuses/public_timeline.xml")
}
xml_data = XmlSimple.xml_in(res.body);
@result = xml_data["status"]
end
I think it can’t be as simple as this. It take url from @source instance variable. @source.url is the url you added when you first created the source from web page. Then it fetches tweets and put the body of the response into XMLSimple library. Lastly I put each entry under into @result instance variable as Array.
sync method
Next is “sync” method. The code repopulated by rhogen command iterates through each entry of @result and put value into ObjectValue object. ObjectValue is a table where you can store the result of API response as key => value pair. This way, you can
The below are the difference between normal ORM(Object Relational Mapping) object table and ObjectValue table.
Normal table
ID | Industry | Name
44e804f2-4933-4e20-271c-48fcecd9450d | Technology | Mobio India
69ae8551-c0bd-0cb3-5c25-48ff9bae965a | Finance | vSpring
ObjectValue table
Object | Attribute | Value
44e804f2-4933-4e20-271c-48fcecd9450d | industry | Technology
44e804f2-4933-4e20-271c-48fcecd9450d | Name | Mobio India
69ae8551-c0bd-0cb3-5c25-48ff9bae965a | industry | Finance
69ae8551-c0bd-0cb3-5c25-48ff9bae965a | Name | vSpring
What I did not understand much was where “entrylist” and “namevalue_list” attributes are coming.
At the rhomobile tutorial
, @result is set like below at query method.
result = client.get_entry_list(session_id,'Account','','',0,['name','industry'],10000,0)
And it also explains that “client” variable is available and not for REST API.
The Rhomobile tutorial page shows example using SugarCRM which uses SOAP API. Since Twitter uses REST API, I referred to Lighthouse and BaseCamp examples which are also Rails based app and uses REST API.
By examining these codes, you notice that there is a helper called RestAPIHelpers . Let’s use that. I included the helper at the top of the class like this
class PublicTimeline < SourceAdapter
include RestAPIHelpers
They use “add_triple” method which also adds your API result into ObjectValue, so I decided to use the method to inject my Tweets.
def sync
@result.each do |item|
item_id = item["id"].first.to_i
item.keys.each do |key|
value = item[key] ? item[key][0] : ""
add_triple(@source.id, id, key.gsub('-','_'), value)
# convert "-" to "_" because "-" is not valid in ruby variable names
end
end
end
This is also simple one. @result contains array of Tweet where each tweet contains data in hash format like below.
{
"text"=>
["@ceetee dude! How's it like out there! Been there before?"],
"id"=>["1187577382"],
"created_at"=>["Sat Feb 07 23:49:04 +0000 2009"]
}
I iterated through each hash key and send its key and value into “add_triple” methods
@result.first.keys
=> ["text", "id", "created_at"]
Now it’s done. Go back to “Editing Source Adapter” page, click “Show records”, then “Refresh object-value records”. This shouldwill show all recent tweets.
Ummm, certain values are missing, such as “user”. By looking at original xml, you should see that certain attributes are nested, so just going through each key of hash won’t work. You need to iterate recursively every time value contains hash.
Final code
Here is the complete code including the change to recursively iterate through each tweets. I refactored the code by separating iteration part into “iteratekeys” and calls the method recursively if the hash value contains another hash. When you call iteratekeys, it also adds prefix, so “user” => {“screenname”} will be stored into “userscreen_name” key name at ObjectValue
class PublicTimeline < SourceAdapter
include RestAPIHelpers
def initialize(source)
super
end
def query
uri = URI.parse(@source.url)
res = Net::HTTP.start(uri.host, uri.port) {|http|
http.get("/statuses/public_timeline.xml")
}
xml_data = XmlSimple.xml_in(res.body);
@result = xml_data["status"]
end
def sync
@result.each do |item|
item_id = item["id"].first.to_i
iterate_keys(:item => item, :item_id => item_id)
end
end
private
def iterate_keys(option)
item = option[:item]
item_id = option[:item_id]
prefix = option[:prefix] || ""
# item.keys => ["user", "favorited", "truncated"...]
item.keys.each do |key|
value = item[key] ? item[key][0] : ""
if value.kind_of?(Hash) && value != {}
# eg. :user => {:url => 'foo} becomes user_url
iterate_keys(:prefix => (prefix + underline(key) + "_"), :item => value, :item_id => item_id)
else
# This method is from rest_api_helper
add_triple(@source.id, item_id, prefix + underline(key), value)
end
end
end
def underline(key)
key.gsub('-','_')
end
end
This is the complete “Show Record”, including nested attributes, such as user_id/name/url
Next
Since we now have a source adapter which loads public tweet, I will explain the code I wrote to display these tweets on iPhone simulator using Rhodes.
Filed under: Rhodes | 47 Comments
Tags: tutorial
Hi Makoto,
Its really good tutorial. I follow each and every step of this tutorial and i got the latest tweets in my local rhosync server. But how can i see those latest tweets in my emulators. From my emulator i posted some public timeline tweets but i cant see them in http://twitter.com/statuses/public_timeline.xml. but i can see those tweets in my emulator. Please guide me…
Thanks in Advance.
Hi, madhan.
Glad to know you were able to follow the tutorial without much problems. I am in the middle of writing tutorial on how to show public tweets on your iphone emulator.
Regarding posting part, it is less likely that you can see your own tweets at public tweets, because Twitter only shows 20 tweets out of million other tweets and it caches for 60 sec.
Please wait until I cover the topic about posting/displaying your private tweets.
Thanks.
Makoto
One thing I noticed is that add_triple requires 5 arguments not sure it this is new.
Should now be?
add_triple(@source.id, id, key.gsub(‘-‘,’_’), value, @source.current_user.id)
Hi, Nick. Thank you for your comment. Yes, you are correct. It used to be like this till 0.2, and it added extra argument at 0.3. I will fix the blog post.
To get everything up and running on my server I added credential to the initialize method and @source.current_user.id to the add_triple rest_api_helper.
Great post 🙂
N
def initialize(source,credential)
super(source,credential)
end
# This method is from rest_api_helper
add_triple(@source.id, item_id, prefix + underline(key), value, @source.current_user.id)
Thanks Nick, I was getting an error stating an invalid number of arguments which your changes to the code fixed but now I am getting:
undefined method `hash_from_data’ for #
Any ideas?
Great tutorial Makoto!
Hello,
I followed this tutorial (part 1) step by step.
After creating PublicTimeLine in “Adapter className” field and
putting url http://twitter.com I created the class Adapter
with >rhogen source PublicTimeline
in rhosync directory (is it the right directory ?).
But then when I click on “Show Records” in my source Adapter
I get the following error :
NoMethodError in SourcesController#show
You have a nil object when you didn’t expect it!
The error occurred while evaluating nil.sync
RAILS_ROOT: /Users/nonenone/testrhomobile/rhosync
Application Trace | Framework Trace | Full Trace
app/models/source.rb:133:in `dosync’
app/models/source.rb:63:in `refresh’
app/controllers/sources_controller.rb:43:in `show’
/Library/Ruby/Gems/1.8/gems/mongrel-1.1.5/lib/mongrel/rails.rb:76:in `process’
/Library/Ruby/Gems/1.8/gems/mongrel-1.1.5/lib/mongrel/rails.rb:74:in `synchronize’
/Library/Ruby/Gems/1.8/gems/mongrel-1.1.5/lib/mongrel/rails.rb:74:in `process’
/Library/Ruby/Gems/1.8/gems/mongrel-1.1.5/lib/mongrel.rb:159:in `process_client’
/Library/Ruby/Gems/1.8/gems/mongrel-1.1.5/lib/mongrel.rb:158:in `each’
/Library/Ruby/Gems/1.8/gems/mongrel-1.1.5/lib/mongrel.rb:158:in `process_client’
/Library/Ruby/Gems/1.8/gems/mongrel-1.1.5/lib/mongrel.rb:285:in `run’
/Library/Ruby/Gems/1.8/gems/mongrel-1.1.5/lib/mongrel.rb:285:in `initialize’
/Library/Ruby/Gems/1.8/gems/mongrel-1.1.5/lib/mongrel.rb:285:in `new’
/Library/Ruby/Gems/1.8/gems/mongrel-1.1.5/lib/mongrel.rb:285:in `run’
/Library/Ruby/Gems/1.8/gems/mongrel-1.1.5/lib/mongrel.rb:268:in `initialize’
/Library/Ruby/Gems/1.8/gems/mongrel-1.1.5/lib/mongrel.rb:268:in `new’
/Library/Ruby/Gems/1.8/gems/mongrel-1.1.5/lib/mongrel.rb:268:in `run’
/Library/Ruby/Gems/1.8/gems/mongrel-1.1.5/lib/mongrel/configurator.rb:282:in `run’
/Library/Ruby/Gems/1.8/gems/mongrel-1.1.5/lib/mongrel/configurator.rb:281:in `each’
/Library/Ruby/Gems/1.8/gems/mongrel-1.1.5/lib/mongrel/configurator.rb:281:in `run’
/Library/Ruby/Gems/1.8/gems/mongrel-1.1.5/bin/mongrel_rails:128:in `run’
/Library/Ruby/Gems/1.8/gems/mongrel-1.1.5/lib/mongrel/command.rb:212:in `run’
/Library/Ruby/Gems/1.8/gems/mongrel-1.1.5/bin/mongrel_rails:281
I also tried to put the adapter file “public_timeline.rb” in rhosync/lib
but it doesn’ t work too :-(.
I am new to ruby and rhosync (but I followed their tutorial in detail) so sorry of I missed an important step.
Thanks for you help !
Thanks for the tutorial,
Bob, i was facing same problem, to resolve i just changed Source name (removed spaces from there). and works perfectly fine.
Also, In this post (Nick Treadway on April 21, 2009 said:) as You have to use “add_triple(@source.id, item_id, prefix + underline(key), value,@source.current_user.id)”.
Thanks again.
Hi,
Will be nice if you can provide links to the next tutorials. Nice job.
Cheers.
i am also having the same problem like Bob, but i did not understand the Niraj Pendal solution. please guide me as quickly as possible.
Thanks in Advance
ghk.
while creating a source there is one option called
file should be in vendor/sync directory or subdirectory
what is meaning of this
Hi,what a beautiful jeans,thanks for sharing.I will get one like that.bill
This code is for old version of rhodes.. To develop for latest rhodes version see: http://itsallaboutruby.blogspot.com/2010/10/creating-rhodes-application-which-will.html
Please help me understand (I am a noobe).
How do I get to the console to edit your source adapter code???? What am I missing?
You say:
register your account, login to the server, and create an application called “Twitter”.
So, where is all that? I don’t see it in the rhosync console. Please explain, I would really love to follow this tutorial.
Thanks,
Use oils that can help keep your hair soft and manageable.
Apparently, it actually lifts up build up from the root and
therefore stimulates hair growth. Almost from the time of our grandparents and other ancestors,
the advantages of using olive oil are known to all.
Ideally, prepare the day before dilution and in the evening, put the mixture
in the palm of your hand and apply and hold your hair and scalp.
The herbal solution is said to be a natural one, but,
this does not necessarily indicate that it is more
effective than other available procedures.
Hi Dear, are you really visiting this website on a regular
basis, if so after that you will without doubt obtain pleasant knowledge.
Howdy! I could have sworn I’ve been to this blog before but after browsing through some of the post I realized it’s new to me.
Anyways, I’m definitely happy I found it and I’ll be bookmarking and checking back frequently!
Quality posts is the crucial to invite the visitors to visit the site, that’s what this web page is providing.
Thank you for some useful tips here, I’ve tweeted regarding your page to my followers.
I’m pretty pleased to uncover this great site. I wanted to thank you for ones time just for this fantastic read!! I definitely enjoyed every bit of it and I have you saved to fav to look at new stuff in your site.
Wow! Thank you! I constantly wanted to write on
my blog something like that. Can I take a part of your post
to my website?
We’re a bunch of volunteers and opening a brand new scheme in our community. Your site offered us with valuable info to work on. You have performed an impressive task and our entire community can be thankful to you.
Thanks for sharing your thoughts about Kia. Regards
After I initially left a comment I appear to have clicked the -Notify me when new comments are added- checkbox and now
each time a comment is added I recieve four emails
with the exact same comment. There has to be a way you are able to remove me
from that service? Thanks a lot!
This piece of writing is in fact a pleasant one it helps new web visitors, who
are wishing for blogging.
My developer is trying to convince me to move to .
net from PHP. I have always disliked the idea because of the costs.
But he’s tryiong none the less. I’ve been using Movable-type on numerous websites for about a year and am
concerned about switching to another platform.
I have heard great things about blogengine.
net. Is there a way I can import all my wordpress posts into
it? Any kind of help would be really appreciated!
Thanks for a marvelous posting! I really enjoyed reading it,
you could be a great author. I will remember to bookmark your blog and will often come back later on.
I want to encourage you continue your great posts, have a nice morning!
Article writing is also a fun, if you be familiar with then you can write if not it is complex to write.
Hiya! Quick question that’s entirely off topic. Do you know how to make your site mobile friendly? My blog looks weird when browsing from my iphone 4. I’m trying to find
a template or plugin that might be able to fix this issue.
If you have any suggestions, please share.
Appreciate it!
Hi, I do think this is an excellent website. I stumbledupon
it 😉 I will come back once again since i have bookmarked it.
Money and freedom is the greatest way to change, may you be
rich and continue to guide others.
I for all time emailed this webpage post page
to all my associates, since if like to read it afterward my
friends will too.
Have you ever thought about adding a little bit more than just your articles?
I mean, what you say is important and all.
However think of if you added some great pictures or video clips to
give your posts more, “pop”! Your content is excellent but with pics and video clips, this
site could undeniably be one of the most beneficial in
its niche. Amazing blog!
When someone writes an paragraph he/she maintains the image of a
user in his/her brain that how a user can be aware of it.
So that’s why this article is amazing. Thanks!
I just couldn’t depart your web site prior to suggesting that I
actually loved the standard information an individual provide on your visitors?
Is gonna be again regularly in order to check up on new
posts
Thanks for the auspicious writeup. It if truth be told was once a enjoyment
account it. Glance complicated to far brought agreeable from you!
However, how could we keep in touch?
Its like you read my mind! You seem to know so much about this, like you wrote the book in
it or something. I think that you could do with a few
pics to drive the message home a bit, but instead of that, this is magnificent blog.
A fantastic read. I will definitely be back.
With havin so much written content do you ever run into any
problems of plagorism or copyright violation? My blog has a lot of unique content I’ve either authored myself or outsourced
but it seems a lot of it is popping it up all over the web without my authorization.
Do you know any techniques to help protect against content from being ripped off?
I’d really appreciate it.
Thanks for every other excellent post. The place else could anyone get that type of info
in such an ideal means of writing? I have a presentation subsequent
week, and I am at the search for such information.
I think the admin of this web page is really working hard for his site, since here every
material is quality based data.
Wake up, weight loss we have to pay oout the truth, sir, he is different.
If the weather’s looking fairly settled round here for a
way tuat suggests he is onn display at tonight’s Home Run Derby is just one kind.
Hair growth can become impaired because of collected
body toxins. While there are many products in the market these days, which claim to
solve your problem of improper hair growth, ranging from
shampoos to conditioners, to hair oils, and even pills and tonics, not many of these actually work.
One of the areas that is affected most is our hair growth and maintenance.
An impressіve shаre! I’ve ϳust forwarded this onto a co-worker
who has been doіng a little research on this. And he in fact bought me lunch duе to the fact that I found it for him…
lol. So allow me to reᴡⲟrd this…. Thanks for the meal!!
But yeah, thanx fߋr spending the time to talk about this subject here on your web page.
is a relied on QQ Online poker Online and also Bandar Ceme Online wagering
site that supplies on the internet card games such as Online Texas Hold’em, DominoQQ, Capsa Online,
Ceme Online, Ceme99, Online Gaming Online Poker Sites.
QQ Casino Poker Ceme, the best and most safe online poker agent site
with 1 day IDN Online Casino poker service.
For those of you Lovers of the game Online Online and that intend to
play betting Online Casino poker, Online Ceme, Domino QQ, City Ceme, Online Gaming,
Bandar Capsa Online in 1 ID