Making Twitter mobile client with Ruby using Rhodes & Rhosync (Part 1)

10Feb09

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&#8221; 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.



47 Responses to “Making Twitter mobile client with Ruby using Rhodes & Rhosync (Part 1)”

  1. 1 madhan

    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.

  2. 2 inouemak

    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

  3. 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)

    • 4 inouemak

      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.

  4. 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)

  5. 6 Amanda MacKenzie

    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!

  6. 7 Bob

    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 !

  7. 8 Niraj Pendal

    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.

  8. 9 joseph

    Hi,
    Will be nice if you can provide links to the next tutorials. Nice job.
    Cheers.

  9. 10 harikrishna

    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.

  10. while creating a source there is one option called
    file should be in vendor/sync directory or subdirectory
    what is meaning of this

  11. Hi,what a beautiful jeans,thanks for sharing.I will get one like that.bill

  12. 13 Abhishek Nalwaya

    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

  13. 14 pmibal

    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,

  14. 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.

  15. Hi Dear, are you really visiting this website on a regular
    basis, if so after that you will without doubt obtain pleasant knowledge.

  16. 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!

  17. Quality posts is the crucial to invite the visitors to visit the site, that’s what this web page is providing.

  18. Thank you for some useful tips here, I’ve tweeted regarding your page to my followers.

  19. 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.

  20. 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?

  21. 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.

  22. Thanks for sharing your thoughts about Kia. Regards

  23. 24 Gita

    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!

  24. This piece of writing is in fact a pleasant one it helps new web visitors, who
    are wishing for blogging.

  25. 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!

  26. 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!

  27. Article writing is also a fun, if you be familiar with then you can write if not it is complex to write.

  28. 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!

  29. 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.

  30. I for all time emailed this webpage post page
    to all my associates, since if like to read it afterward my
    friends will too.

  31. 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!

  32. 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!

  33. 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

  34. 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?

  35. 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.

  36. 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.

  37. 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.

  38. I think the admin of this web page is really working hard for his site, since here every
    material is quality based data.

  39. 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.

  40. 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.

  41. 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.

  42. 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


  1. 1 image tasks management google voice app android
  2. 2 road Bikes Cheap ebay
  3. 3 helen-elizabeth
  4. 4 dynah

Leave a reply to Annetta Cancel reply