Fire Eagle Sample App – whereis

Fire Eagle (not fireeagle) is a new location platform from some of my colleagues here at Yahoo! Brickhouse. I’ve put together a simple sample app called whereis which shows off the power of the platform.

What is whereis?

Each instance of whereis lets you share location within a trusted group of people. Whereis does not update location in Fire Eagle, you should do this using other apps (eg. Fire Eagle automatic device updaters, or other sites). This screenshot basically sums it up -

whereis screenshot

What can I use it for?

  • Run it on your intranet to easily share location between your co-workers
  • Run it on the internet so friends and family can share location

Who is whereis intended for?

Whereis is intended for developers who have familiarity with Ruby and access to a server.

Why are you releasing it?

It is a simple app (~200 lines of code) built on top of the camping framework. If you understand Ruby and the MVC pattern, it will give you a good idea of how FE works and can be used. I think the code is pretty self explanatory, drop me a line if you have any suggestions.

What else should I do with it?

  • run an instance for your group
  • use it to experiment with fireeagle
  • skin it with CSS to make it look good
  • build a service around it
  • add a subscriptions table with acls to have a twitter like mode
  • anything else

How do I use it?

  • download the files
  • make sure you and everyone you want to use whereis have fireeagle accounts
  • install camping (via gem)
  • install dependencies
  • get a fireeagle consumer token and secret from the FE website and set up the correct callback URL
  • insert the token and secret in getFEClient – whereis.rb
  • change the login password in createTheBasics – whereis.rb
  • “camping whereis.rb” on the command line

Caveats

  • Whereis uses sqlite as it’s database so it’s probably not suitable for large scale deployments.
  • I’m using a very early version of the fireeagle gem, before you write anything make sure to look at the newest version.
  • This code might be a bit slow in production if the fireeagle servers are heavily loaded. If you want to take initiative and fix the problem this is how you would do it (via Seth Fitzsimmons) – instead of updating the locations on query, update them periodically using a cron job or some other method.

#!ruby
#whereis - a simple fireeagle demo
#IMPORTANT: make sure to use the version of fireeagle.rb included in the tarball
#Nikhil Bobb (nikhilb at yahoo-inc.com)
#26th Feb 08

require 'camping/session'
require 'FireEagle'
require 'JSON'

Camping.goes :Whereis

module Whereis
  include Camping::Session
  table_style = "border-width:1px; border-style:outset"

  def getFEClient(access_token = "", access_token_secret = "")
    client = FireEagle::Client.new \
	  :consumer_key        => "",
	  :consumer_secret     => "",
	  :access_token        => access_token,
          :access_token_secret => access_token_secret
  end

  #gets a user's location from fireEagle and returns a nice
  #string
  def getFELocation(access_token, access_token_secret)
    client = getFEClient(access_token, access_token_secret)
    location_obj = client.user
    #query the JSON object to get the location
    #where best guess = true
    hierarchy = location_obj["user"]["location_hierarchy"]
    current_loc = hierarchy.select {|l| l["best_guess"]}.first
    [current_loc["name"], current_loc["located_at"]]
  end
end

module Whereis::Models

  class User < Base;
    has_one :request_token
    serialize :access_token
  end

  class RequestToken < Base;
    belongs_to :user
    serialize :bin
  end 

  class Login < Base; end

  class CreateTheBasics < V 1.0
    def self.up
      create_table :whereis_logins do |t|
        t.column :username, :string
        t.column :password, :string
      end
      create_table :whereis_users do |t|
        t.column :name, :string
        t.column :access_token, :text
      end
      create_table :whereis_request_tokens do |t|
        t.column :str, :string
        t.column :bin, :text
        t.column :user_id, :integer
      end
      Login.create :username => 'admin', :password => "eagle123"
    end
    def self.down
      drop_table :whereis_users
      drop_table :whereis_request_tokens
      drop_table :whereis_logins
    end
  end
end

module Whereis::Controllers
  class Style < R '/styles.css'
    def get
      @headers["Content-Type"] = "text/css; charset=utf-8"
      @body = %{
        table {
          border-width:1px;
          border-style: dotted;
        }
        td {
          border-width: 1px 1px 1px 1px;
          border-style: solid;
        }
      }
    end
  end
  class Auth < R '/auth'
    def post
      @logged_in_user = Login.find :first, :conditions => ['username = ? AND password = ?', input.username, input.password]
      if @logged_in_user
        @login = 'login success !'
        @state.user_id = @logged_in_user.id
        redirect R(Index)
      else
        @login = 'wrong user name or password'
        render :id
      end

    end
    def get
      render :id
    end
  end

  class Index < R '/'
    def get
      if @state.user_id.blank?
        redirect R(Auth)
        return
      end

      @users = []
      User.find(:all).each do |u|
        t = u.access_token
        if t
          location = getFELocation(t.token, t.secret)
          @users << [u.name, location[0], location[1]]
        end
      end
      render :whereis
    end

    def post
      if @state.user_id.blank?
        redirect R(Auth)
        return
      end

      client = getFEClient
      request_token = client.get_access_token_part1
      rt = RequestToken.create :bin => request_token, :str => request_token.token
      u = User.create :name => input.user, :request_token => rt
      redirect "#{FireEagle::AUTHORIZATION_URL}?oauth_token=#{request_token.token}"
    end
  end

  class Callback < R '/callback'
    def get

      if @state.user_id.blank?
        redirect R(Auth)
        return
      end

      client = getFEClient
      rt = RequestToken.find_by_str(input.oauth_token)
      at = client.get_access_token_part2(rt.bin)
      user = rt.user
      user.access_token = at
      user.save
      rt.destroy
      redirect "/"
    end
  end
end

module Whereis::Views
  def layout
    html do
      head do
        title { "Where is everyone?" }
        link :rel => 'stylesheet', :type => 'text/css',
             :href => '/styles.css', :media => 'screen'
      end
    end
    body { self << yield }
  end

  def id
    p @login
    form :action => R(Auth), :method => 'post' do
      label 'Username', :for => 'username'; br
      input :name => 'username', :type => 'text'; br

      label 'Password', :for => 'password'; br
      input :name => 'password', :type => 'text'; br

      input :type => 'submit', :name => 'login', :value => 'Login'
    end

  end

  def whereis
    form :method => "post" do
      input :type => "text", :name => "user"
      input :type => "submit", :value => "add me"
    end
    table do
      tr do
        td "name"
        td "location"
        td "time"
      end
      tr do
        @users.each do |u|
          u.each{|c| td c}
        end
      end
    end
  end
end

def Whereis.create
  Camping::Models::Session.create_schema
  Whereis::Models.create_schema :assume => (Whereis::Models::User.table_exists? ? 1.0 : 0.0)
end
Digg this     Create a del.icio.us Bookmark     Add to Newsvine

2 Responses to “Fire Eagle Sample App – whereis”

  1. James Says:

    Cool stuff. And nice new style for your blog!

  2. Barce Says:

    I hope your clean code convinces folks at Yahoo to move away from PHP to Ruby. I didn’t get Fire Eagle until I read this. Thanks!

Leave a Reply