titi: Agile Monkeying around with Activity Streams

Titi is a first stab at a universal ActivityStream interface.

How you can help

  • Give the coders something to target: a) Pick an API or feed to add. For ideas, choose one not yet implemented from “Supported Feeds” list, below. b) Get a sample of the raw API or feed output (use your own account so noone files a copyright violation bug report). c) create an appropriately-named file, say spec/data/providers/PROVIDER_NAME/PROVIDER_FEED_NAME-raw_feed.xml or spec/data/providers/PROVIDER_NAME/PROVIDER_FEED_NAME-raw_api.json or whatever. d) If that feed is not yet in activity_streams format, use the raw sample to create an example activity_streams output. e) Issue a pull request.
  • Take an API or feed that has example input and output and code its interface.

Example

Let’s define a super-simple twitter user and status model:


    User = Struct.new(
      :id, :screen_name, :protected, :followers_count, :friends_count, :statuses_count, :favourites_count, :created_at,
      :name, :url, :location, :description, :time_zone, :utc_offset,
      :profile_background_color, :profile_text_color, :profile_link_color, :profile_sidebar_border_color, :profile_sidebar_fill_color, :profile_background_tile, :profile_background_image_url, :profile_image_url
      )

    Status = Struct.new( :id, :created_at, :user, :favorited, :truncated,
      :in_reply_to_user_id, :in_reply_to_status_id, :text, :source,
      :in_reply_to_screen_name )

With no other work, we can pull a raw JSON hash off the wire and create model instances:


    raw_json_resp = RestClient.get("http://twitter.com/statuses/show/12233609555.json")
    Twitter::Status.adapt(JSON.load(raw_json_resp.to_s))
    #=> #<struct Titi::Provider::Twitter::Status id=12233609555, created_at="Thu Apr 15 17:01:52 +0000 2010", user=#<struct Titi::Provider::Twitter::User id=1468401, screen_name="sockington", protected=false, followers_count=1520814, friends_count=457, statuses_count=6234, favourites_count=2, created_at=Mon, 19 Mar 2007 03:45:00 +0000, name="Sockamillion", url="http://www.sockington.org/", location="Waltham, MA", description="I am Jason Scott's Cat.", time_zone="Eastern Time (US & Canada)", utc_offset=-18000, profile_background_color="48484c", profile_text_color="000000", profile_link_color="000000", profile_sidebar_border_color="79c021", profile_sidebar_fill_color="585e7e", profile_background_tile=false, profile_background_image_url="http://a1.twimg.com/profile_background_images/6682718/SocksTwitter.jpg", profile_image_url="http://a3.twimg.com/profile_images/77537329/IMG_3738_normal.JPG">, favorited=false, truncated=false, in_reply_to_user_id=nil, in_reply_to_status_id=nil, text="THANK GOODNESS THE LIBRARY OF CONGRESS HAS UNDERSTOOD THE IMPORTANCE OF MY TWEETS what do you mean others are getting in too", source="<a href=\"http://apiwiki.twitter.com/\" rel=\"nofollow\">API</a>", in_reply_to_screen_name=nil>

Titi lets you write a straightforward and compact adaptor to activity stream format:


    class Titi::Provider::Twitter::Status
      def to_activity_stream_entry
        ActivityStreams::Entry.adapt(
          :id        => %Q{tag:twitter.com,2007:http://twitter.com/#{user.screen_name}/statuses/#{id}},
          :title     => text,
          :content   => text,
          :published => created_at,
          :verb      => :post
          ) do |entry|
          entry.has_author user.name, user.url
          entry.has_obj do |activity_obj|
            activity_obj.id        = id
            activity_obj.title     = text
            activity_obj.published = created_at
            activity_obj.updated   = created_at
            activity_obj.author    = entry.author
          end
        end
      end
    end

Where the mapping is direct, you can use an attribute => attribute hash, as shown in the call to ActivityStreams::Entry.adapt. For anything more complex, you can run arbitrary code in the method’s block (the part within the do … end segment)

Here’s example output. note: this isn’t canonical activity streams format: the XML serialization needs work.


    tweet = Twitter::Status.get(12233609555)
    entry = tweet.to_activity_stream_entry
    puts entry.to_xml
    
        <?xml version="1.0" encoding="UTF-8"?>
        <entry>
          <verb type="symbol">post</verb>
          <mood nil="true"></mood>
          <category nil="true"></category>
          <author>
            <name>Sockamillion</name>
            <uri>http://www.sockington.org/</uri>
          </author>
          <title>THANK GOODNESS THE LIBRARY OF CONGRESS HAS UNDERSTOOD THE IMPORTANCE OF MY TWEETS what do you mean others are getting in too</title>
          <actor nil="true"></actor>
          <published type="datetime">2010-04-15T17:01:52+00:00</published>
          <rank nil="true"></rank>
          <sync nil="true"></sync>
          <id>tag:twitter.com,2007:http://twitter.com/sockington/statuses/12233609555</id>
          <content>THANK GOODNESS THE LIBRARY OF CONGRESS HAS UNDERSTOOD THE IMPORTANCE OF MY TWEETS what do you mean others are getting in too</content>
          <target nil="true"></target>
          <link nil="true"></link>
          <object>
            <author>
              <name>Sockamillion</name>
              <uri>http://www.sockington.org/</uri>
            </author>
            <title>THANK GOODNESS THE LIBRARY OF CONGRESS HAS UNDERSTOOD THE IMPORTANCE OF MY TWEETS what do you mean others are getting in too</title>
            <published>Thu Apr 15 17:01:52 +0000 2010</published>
            <id type="integer">12233609555</id>
            <vevent nil="true"></vevent>
            <content nil="true"></content>
            <link nil="true"></link>
            <updated>Thu Apr 15 17:01:52 +0000 2010</updated>
            <object-type nil="true"></object-type>
          </object>
          <source nil="true"></source>
          <updated nil="true"></updated>
        </entry>

Supported Feeds

The goal is to cover all the following services. Thanks to Cliqset and Rob Dolin for the list:


  :social           => %w[ AvatarsUnited   Bebo            Brightkite      Friendster      Friendfeed      GamerDna        Gather          Hi5             Hyves           Identica
                           Jaiku           Multiply        Myspace         Netlog          Plurk           Raptr           Skyrock         Twitter         Facebook        LinkedIn        Plaxo            Xing                                      ],
  :blogging         => %w[ Ameba           Baidu           Blogger         Douban          Livejournal     Posterous       Tumblr          Wordpress       Xanga           TypePad         MoveableType    WlSpaces                                   ],
  :bookmarking      => %w[ Delicious       Digg            Diigo           GoogleReader    Hatena          Meneame         Mixx            Newsvine        Propeller       Reddit          Stumbleupon     Twine                                      ],
  :music            => %w[ Blipfm          Buzznet         Ilike           Lastfm          Pandora         Zooomr          Zune                                                                                                                       ],
  :video            => %w[ BuddyTv         TwelveSeconds   Bliptv          Cinchcast       DailyMotion     FunnyOrDie      Hulu            Joost           Metacafe        Qik             Revver          Vimeo           Youtube                    ],
  :photos           => %w[ DeviantArt      Flickr          Fotolog         MobyPicture     Photobucket     Picasa          Photocase       Slideshare      Smotri          Smugmug         Visualizeus     MetroFlog                       WlSkyDrive ],
  :reviews          => %w[ Blippr          Corkd           Flixster        Goodreads       Librarything    Qype            Readernaut      Yelp                                                                                                       ],
  :messengers       => %w[ AIM                             WlMessenger                                                                                                                                                                                ],
  :location         => %w[ Gowalla         Foursquare      Tripit          UrbanSpoon       Whirl                                                                                                                                                     ],
  :gaming           => %w[ Zynga           Playdom         MsgrGames                                                                                                                                                                                  ],

Copyright

Copyright © 2010 Infochimps, Inc. Available under Apache license: see LICENSE for details.