Archive for the 'ruby' Category

Sketching in VOIP with Tropo

Tropo

At eComm Jeevan and I heard some intriguing stuff about a service called Tropo, a cloud-based scriptable VOIP system designed to let web hackers start working in the voice and telephony world.

The app provides a pretty damn slick interface to quickly set up a real phone number (plus SIP plus Skype, all in one place), then assign it to a script written in javascript, groovy, python, ruby, or probably whatever other BSF language they support.

Here’s one I wrote and deployed in about 20 minutes to read off all the Miniature Monster descriptions from the RSS feed (Tropo calls highlighted). You can test it out by calling (650) 273-5382 (or +99000936 9991428654 on Skype, though I haven’t tried that):


require 'net/http'
require 'rexml/document'

answer
wait(3000)
say "hello, welcome to professor engd's miniature monster hotline. here are your monster updates."
wait(1000)

url = "http://feedproxy.google.com/miniaturemonsters"
xml_data = Net::HTTP.get_response(URI.parse(url)).body
xml_doc = REXML::Document.new(xml_data)

xml_doc.elements.to_a( "//description" ).reverse.each do |desc|
  if (m = (/\<div id=\"description\">\s*\<em\>(.*?)\<\/em\>\<\/div\>/i).match(desc.text))
    say m[1]
    wait(1000)
  end
end

hangup

I’ve worked on some projects recently involving Asterisk-to-web-to-hardware style hacking, typically using something like Gizmo to register a regular phone number assigned to a SIP number, then setting up asterisk as the SIP softphone and messing around with a retarded dialplan syntax to get things going. Tropo seems to simplify all that… substantially.

The documentation is solid if a little thin, but it’s so new, and the app is so slick and well done, so I’m sure there’s more coming. It’s unclear at this point which Ruby libraries and gems they support, if any. And it all feels a little like a toy, but no more than, say, Google App Engine, and for me it’s actually more potentially useful. What I can say at this point is that it’s a solid way to sketch your telelphony ideas quickly and with zero setup time, which is very cool. Are you listening ITP?

Anyway, happy voip hacking.

Gentrify

Tired of stepping over homeless people on your way to bikram? Jonesing for a Yacon Root smoothie? Gentrify helps the elite urban bourgeois find their natural habitats.

Gentrify is currently in alpha lite preview stealth mode edition. Created for Rails Rumble ‘08 by 734m: Huned Botee, Brent Fitzgerald, Visnu Pitiyanuvath, and Gerad Suyderhoud.

How to write a Promiserver promise

This post is my first attempt to explain how to write Promiserver code. This will eventually also find its way into a Promiserver help section, but I thought I’d try it out here first.

Breach and Success

There are two reserved methods: breach and success. Each takes an optional message parameter. Your code defines the conditions under which the promise breaches or succeeds. For example, a really simply promise would be:

if condition_x
  success "it happened!"
end

The value of the condition_x variable determines whether the promise evaluates to success. When condition_x evaluates to true, the promise will be evaluated as a success, with corresponding message “it happened!”. A marginally more sophisticated promise might look like this:

if condition_x
  success "x happened!"
elsif condition_y
  breach "sorry, y happened"
end

In this one we have possibility of a breach as well, if condition_y evaluates to true.

Promise State

Those variables like condition_x and condition_y aren’t defined anywhere in this code. This means that they are free variables.

All these free variables in your promise code are extracted as you write the promise, and together form the state of the promise. When writing the promise, you can change the values of these variables to the desired initial state. Once published and signed, all participants can also change the values of these variables.

Time

Most promises are commitments with respect to some future action or event. So it may make sense to take time into account.

if document_received
  success "thanks, nice work"
elsif  Time.now > document_deadline
  breach "missed the deadline
end

Again, document_deadline and document_received become promise state variables, meaning they can be changed by the participants. If we wanted to fix the deadline and remove it from the state, we could simply add in a line.

document_deadline = Time.parse("5/11/2007 6:00pm")
if document_received
  success "thanks, nice work"
elsif  Time.now > document_deadline
  breach "missed the deadline
end

Keep in mind that Time.now evaluates differently each time the promise is evaluated. Future versions of Promiserver will have a timeline or calendar view, to try out the code for different times in the future.

Ruby Niceness

Since we’re using Ruby with some special activesupport magic, we can use some cool shortcuts. For example, say we want to allow for an extension:

...
if Time.now > (final_date = deadline + extension.days)
  breach "missed the extended deadline #{final_date}"
end
...

This is just a starting point. I have a few people using the system now, and in particular Burak has been a great tester, authoring some promises that have pushed Promiserver a little our of its comfort zone. So we’ll see how this contract-as-code idea continues to develop.

Promiserver ultra-alpha omega supreme

Promiserver

Check it out: promise.media.mit.edu. Borrowing the slogan from OpenCode, This is so alpha, you won’t even want to use itâ„¢

Well, it’s been a while coming, and now I have a super alpha version of Promiserver live and ready for you to make all your programmed commitments with that same desperate, wanton abandon we saw in the early field tests. It’s got bugs aplenty, as well as interface problems, but I can fix em as we find em. Tags coming soon, plus REST API for creating promises programmatically, which is where I think a lot of the applications will be. A lot of other updates too, providing I have enough time given this whole writing ordeal I’m still in the middle of (sigh).

Tiny Drawing

Inspired by the flurry of 13×13 icon creation, Luis and I have been at work the past day on a new, super low resolution icon creation system called Tiny. I’ve written a javascript version with a Rails backend that uses RMagick to produce the PNGs. Meanwhile Luis has thrown together a nice little applet, which I have to admit is a tad more sophisticated than my version. We’ll integrate them in the next few days. It’ll be pretty easy, since creating a new icon is as easy as sending an HTTP POST with a data parameter of 169 0’s or 1’s. Title and parent parameters are taken optionally.

Forever alpha at http://tiny.media.mit.edu/

Tiny

The excitement will really never end. There are 2^169 possible images! Thanks to Amber for the original inspiration with her Mini app for OS.

Friendly Ruby (Quiz Solution)

My solution to the recent Ruby Quiz #91 is friendly.rb, a module that allows a user to interactively add method definitions at runtime.

module Friendly

  def method_missing name
    @_new_methods ||= Hash.new
    unless @_new_methods.has_key? name
      prompt_for_definition name
    end
    eval @_new_methods[name]
  end

  def prompt_for_definition name
    puts "It appears that #{name} is undefined."
    puts "Please define what I should do (end with a blankline):"
    @_new_methods[name] = ""
    while $stdin.gets !~ /^\s*$/
      @_new_methods[name] << $_
    end
  end

  def added_methods
    @_new_methods.keys
  end

  def added_method_definitions
    @_new_methods.map {|k,v|
      s = "def #{k}\n  "
      v.rstrip!
      s << v.gsub("\n", "\n  ")
      s << "\nend"
    }
  end

end

Example usage, in this case just using Friendly to extend the top level object:

irb(main):001:0> include Friendly
=> Object
irb(main):002:0> foo = bar * z
It appears that bar is undefined.
Please define what I should do (end with a blankline):
12

It appears that z is undefined.
Please define what I should do (end with a blankline):
4

=> 48
irb(main):003:0> foo
=> 48
irb(main):004:0> bar
=> 12
irb(main):005:0> z
=> 4
irb(main):006:0> added_methods
=> [:z, :bar]
irb(main):007:0> puts added_method_definitions.join("\n\n")
def z
  4
end

def bar
  12
end
=> nil
irb(main):008:0>

Acts As Permalinkable

I’ve released the polymorphic acts_as_permalinkable plugin that I developed for use with PLWire. To install:

./script/plugin discover \
   http://plw.media.mit.edu:9090/repository/public/rails/plugins
./script/plugin install -x acts_as_permalinkable
./script/generate permalinkable_migration
rake migrate

Then just add the acts_as_permalinkable snippet to your models:

class Article < ActiveRecord::Base
  acts_as_permalinkable
  ...
end

class Photo < ActiveRecord::Base
  acts_as_permalinkable
  ...
end

When you save your model the permalink will automatically update as well. By default it uses a title attribute to create the slug and the created_on attribute for the date, but this is easily customized. For example to use it with the name attribute and the updated_at attribute:

  acts_as_permalinkable \:on => :name,
                         :use_date => :updated_at

You can also provide a Proc that filters the permalinkable title field before it is turned into a slug. For example maybe you want to strip the tags and only take the first 20 characters:

  acts_as_permalinkable \:on => :content,
      :slug_modifier => lambda { |x|
             x.gsub(/\<.+?\>/, "").strip[0..20]
          }

Within your views you can link to the permalinks with a few url helper methods, url_for_permalink and link_to_permalink:

<%= url_for_permalink @article,
    {:action => "permalink"},
    {:class => "permalink",
     :title => "Permalink for '#{@article.title}'"}
%>

<%= link_to_permalink "permalink", @article,
    {:action => "permalink"},
    {:class => "permalink",
     :title => "Permalink for '#{@article.title}'"}
%>

And within your controller just use the find_permalinked method:

find_permalinked :slug => "this-is-a-slug",
    :year => 2006,
    :month => 8,
    :day => 7

Underspecifying these parameters will return a collection for all matching records. Or if your parameters already have these values it is pretty easy:

find_permalinked params

You can add permalinks to existing records with the create_all_slugs singleton method. I use the console. For example, with an Article model:

Article.create_all_slugs

Hope it works out to be helpful for at least a few people out there.