Home > Tags > ruby

ruby

Rubigraph

Summary

I wrote a Rubigraph, a Ruby wrapper for Ubigraph .

What is Ubigraph

I recently found Ubigraph, a nice visualization tool. Though I’m also big fan of Graphviz, which generates static graph visualization, I got falling in love with Ubigraph because it’s so dynamic. Ubigraph can add, remove and change nodes, edges, and their attributes. It’s amazing! See demonstration movie.

It is based on client/server architecture. Though the server source code is closed, its XML-RPC API is open, and we can write our client application easily. It has Mac OS X and Linux binaries.

What is Rubigraph

But, I cannot put up with writing XML-RPC by referencing API manual every time ! So I made a small Ruby wrap for it, and released it as Rubigraph.

Install

Type gem install rubygraph. Its place in Rubyforge is here.

Source code

How do I write a graph

require 'rubigraph'
 
Rubigraph.init        # initialize XML-RPC client.
 
v1  = Vertex.new
v2  = Vertex.new
e12 = Edge.new(v1, v2)
 
v1.color  = '#003366'
v2.shape  = 'sphere'
e12.label = 'edge between 1 and 2'

It seems easy, isn’t it?

Conclusion

It’s a fun to visualize some data dynamically. I’m using it for tracing function calls, feeling so convinient. Ubigraph++.

Write a Quicksilver plugin with RubyCocoa

I wrote a Quicksilver plugin with RubyCocoa, that adds “hello world” to the passed string.

  • Xcode project codes : in CodeRepos
  • License : revised BSD

It should work on Tiger/Leopard if RubyCocoa is installed.

How does it work

As I mentioned in QuartzComposer CustomPatch with RubyCocoa, we just call RBBundleInit() function in plugin initializaiton phase to write some plugin with RubyCocoa. But wait, where should be the initializaiton code in Quicksilver plugin ?

We write actual plugin behavior in the class that inherits QSActionProvider, so I tried to call RBBundleInit() in the init() method in that class… however, that resulted in crashing Quicksilver :(

Then I called RBBundleInit() at only first time in performActionOnObject() of the actual Action class, and made references between Objective-C and Ruby class instances.

After that, I implemented an actual action behavior in Ruby class, and delegates from Objective-C to Ruby method, thats’ all. This is ugly, confusing, … I know, but it does work well :)

Code Snippets

ActionProvider in Objective-C:

 (QSObject *)performActionOnObject:(QSObject *)dObject{
  // initialize RubyCocoa
  static bool loaded = false;
  if (!loaded) {
    if (RBBundleInit("qs_action.rb", [self class], self)) {
      NSLog(@"[RubyCocoaPluginAction.performActionOnObject] RBBundleInit failed"
);
      abort();
    }
    loaded = true;
  }
 
  // delegate actual action to Ruby class
  QSObject *ret = [QSObject objectWithString:[rb_ act:dObject]];
  return ret;
}

RubyCocoa side:

class Action
  def initialize(logger)
    @logger = logger
  end
 
  # write something great :)
  #  - arg : QSObject
  def act(arg)
    val = arg.stringValue
    @logger.info(val)
    'Hello world, ' + val
  end
end # Action
 
require 'osx/cocoa'
OSX.init_for_bundle do |bdl, owner, log|
  # bdl    - the bundle related with the 2nd argument of RBBundleInit
  # owner  - the 3rd argument of RBBundleInit as optional data
  # log    - logger for this block
 
  act = Action.new(log)
  owner.setInstance act
end

Future works

  • Better initializaiton. It should be in constractor of some class)
  • Inherits QSActionProvider by Ruby class (more pure Ruby)
  • Use ns_import ?

It should not be “With RubyCocoa”, but rather “By RubyCocoa”.

Conclusion

I made a start point to write a Quicksilver plugin by RubyCocoa. It is timing of initialization that is to be considered about writing some bundle in RubyCocoa. I made it in this article somehow. This article is for someone who wants to create Quicksilver plugin by Ruby, not learning unfamiliar Objective-C.

Reference

user_timeline_to_ical

What is this ?

A simple Ruby script that generates iCalendar format from Twitter user_timeline.

Download

user_timeline_to_ical.rb

Requirements

following gems installed:

  • json
  • icalendar

How to use

simply type

ruby user_timeline_to_ical.rb [your_screen_name]

to print out recent 20 events in iCalendar format to stdout.

Redirect its output to a file, then import it with iCal or Google Calendar.

Screenshot

from iCal: Twitter to iCalendar-01

from Google Calendar: Twitter to iCalendar-02

Future …

I’m developing a web service that serves iCalendar feed (by sending direct message to tracking bot). You will subscribe its URL to see your past behaviors at some time.

Code

Try it !

#
# create iCalendar from recent 20 user_timeline.
#
# usage
#   ruby user_timeline_to_ical.rb [username] > some.ics
#
require 'rubygems'
require 'json'
require 'icalendar'
require 'uri'
require 'open-uri'
require 'kconv'
require 'nkf'
require 'logger'
 
KCODE = 'u'
 
# XXX:
#   quick fix to avoid charset crash
module Icalendar
  class Component < Icalendar::Base
    def print_properties
      s = ""
 
      @properties.each do |key,val| 
        # Take out underscore for property names that conflicted
        # with built-in words.
        if key =~ /ip_.*/
          key = key[3..-1]
        end
 
        # Property name
        unless multiline_property?(key)
           prelude = "#{key.gsub(/_/, '-').upcase}" +
 
           # Possible parameters
           print_parameters(val) 
 
           # Property value
           value = ":#{val.to_ical}" 
           escaped = prelude + value.gsub("\\", "\\\\").gsub("\n", "\\n").gsub(",", "\\,").gsub(";", "\\;")
           #escaped = value
           #s << escaped.slice!(0, MAX_LINE_LENGTH) << "\r\n " while escaped.size > MAX_LINE_LENGTH # XXX : quick fix to avoid charset crash
           s << escaped << "\r\n"
           s.gsub!(/ *$/, '')
         else 
           prelude = "#{key.gsub(/_/, '-').upcase}" 
            val.each do |v| 
               params = print_parameters(v)
               value = ":#{v.to_ical}"
               escaped = prelude + params + value.gsub("\\", "\\\\").gsub("\n", "\\n").gsub(",", "\\,").gsub(";", "\\;")
               s << escaped.slice!(0, MAX_LINE_LENGTH) << "\r\n " while escaped.size > MAX_LINE_LENGTH
               s << escaped << "\r\n"
               s.gsub!(/ *$/, '')
            end
         end
      end
      s
    end
  end
end
 
class UserTimeLine
  attr_accessor :id, :since
  URL = 'http://twitter.com/statuses/user_timeline/'
 
  def initialize(id)
    @id = id
    #@log = Logger.new('debug.log')
  end
 
  def get(url)
    JSON.parse(open(url).readlines.join).sort { |a, b| a['id'] <=> b['id'] }
  end
 
  def url(*arg)
    url = URL + @id + '.json'
    return url if arg.empty?
 
    hash = arg[0]
    args = {}
 
    args['page'] = hash[:page] if hash[:page]
    if hash[:since]
      if String == hash[:since].class 
        args['since'] = URI.escape(hash[:since].split.join('+'))
      else # should be Number
        args['since_id'] = hash[:since]
      end
    end
    arg = args.collect { |k, v| [k, v].join('=') }.join('&')
 
    url + '?' + arg
  end
 
  def to_ical
    msgs = get(url).collect {|x| [x['text'], x['created_at']]}
    cal = Icalendar::Calendar.new
 
    (msgs.size-1).times do |i|
      cal.event do
        dtstart  DateTime.parse(msgs[i][1]).to_ical(true)
        dtend    DateTime.parse(msgs[i+1][1]).to_ical(true)
        summary  msgs[i][0]
      end
    end
 
    cal.to_ical
  end
end
 
 
utl = UserTimeLine.new(ARGV.shift)
print NKF.nkf('-w -Lw', utl.to_ical)

Home > Tags > ruby

Feeds

Return to page top