« June 2008 | Main | September 2008 »

July 21, 2008

Browse Ruby documentation with improved gem server

Firefox screenshot I am currently using Netbeans as my Ruby IDE. The autocompletion feature provides some RDoc snippets but does not always work.

The second option is to use some web resources like http://ruby-doc.org/ or directly use google. But this obviously does not work offline and possibly provides the wrong version of the documentation for those living on the bleading edge. For example, I use the "nightly" RSpec version installing it directly from the git hub.

So I stuck to the local gem server. Unfortunately it's start page is not so convenient, so I've patched the rubygems/server.rb. You can append following near the end of file:

# extended by Vladimir Dobriakov, http://www.innoq.com/blog/vd/
rdoc_proc = lambda do |req, resp|
    gem_name = req.query['gem']
    found_gems = Dir.glob("#{@gemdir}/doc/#{gem_name}*")
    if found_gems.length == 1
        new_path = File.basename(found_gems[0])
        resp.status = 302
        resp['Location'] = "/doc_root/#{new_path}/rdoc/index.html"
    else
        found_gems = Dir.glob("#{@gemdir}/doc/*") if found_gems.empty?
        resp.body = '<ul>'
        found_gems.each do |gem_name|
            path = File.basename(gem_name)
            url = "/doc_root/#{path}/rdoc/index.html"
            resp.body << "<li style='margin-top: 1em'><a href='#{url}'>#{path}</a></li>"
        end
        resp.body << '</ul>'
        resp['Content-Type'] = 'text/html'
    end
end
@server.mount('/rdoc', WEBrick::HTTPServlet::ProcHandler.new(rdoc_proc))

just before

    trap("INT") { @server.shutdown; exit! }
    trap("TERM") { @server.shutdown; exit! }

    @server.start
  end
end

Now the new code is tied to urls like http://localhost:3333/rdoc?gem=active providing either a list of the links to the documentation for the gems, which names start with 'active' or directly jumping to the rdoc of the gem in case there is only one search hit.

In my del.icio.us Plugin for the Firefox I connected http://localhost:3333/rdoc?gem=%s to rdoc keyword. So now I can simply type in something like rdoc httpauth

UPDATE. One additional trick. Run

# please adjust paths accordingly
# use for example 'locate yaml.rb' and 'gem environment'
# for identifying directories

# install ruby sources
cd /usr/src
sudo apt-get source ruby

rdoc -o /usr/lib/ruby/gems/1.8/doc/core/rdoc /usr/lib/ruby/1.8 ruby1.8-1.8.7.72

to generate the rdoc for the core ruby libraries (not gems). The -o parameter specifies the output directory. Two directory names, that follow, specify the ruby- and the C sources respectively.

And how do you browse the ruby documentation?

Posted by VladimirDobriakov at 7:40 PM | Comments (0)

July 3, 2008

Cutting corners with xmpp4r-simple

xmpp4r-simple aims to provide a wrapper around the powerful and well maintained xmpp4r library, "making it dead-simple to send and receive Jabber messages".

Unfortunately, the abstraction and simplification provided by xmpp4r-simple is leaky, missing the principles of jabber protocol. The result - the naive implementation (from the tutorial) did not work for me:

# Send a message to a friend, asking for authorization if necessary:
im = Jabber::Simple.new("[email protected]", "password")
im.deliver("[email protected]", "Hey there friend!")

Yes, I did replace the example.com with the name of my server. ;-)

Result - response 406 "Not Acceptable"

The problem

Jabber::ClientAuthenticationFailure: : Not Acceptable
from /usr/lib/ruby/gems/1.8/gems/xmpp4r-0.3.2.99/lib/xmpp4r/client.rb:116:in `auth'
from /usr/lib/ruby/gems/1.8/gems/xmpp4r-simple-0.8.7/lib/xmpp4r-simple.rb:391:in `connect!'
from /usr/lib/ruby/gems/1.8/gems/xmpp4r-simple-0.8.7/lib/xmpp4r-simple.rb:322:in `client'
from /usr/lib/ruby/gems/1.8/gems/xmpp4r-simple-0.8.7/lib/xmpp4r-simple.rb:331:in `send!'
from /usr/lib/ruby/gems/1.8/gems/xmpp4r-simple-0.8.7/lib/xmpp4r-simple.rb:147:in `status'
from /usr/lib/ruby/gems/1.8/gems/xmpp4r-simple-0.8.7/lib/xmpp4r-simple.rb:90:in `initialize'
from (irb):3:in `new'
from (irb):3

The trace

So I had to go couple of abstraction layers deeper to get it running. At the end I am glad about that. The best opportunity to learn a protocol - to read the documentation in connection with the problem at hand and by observing the actual communication.

Using Gajim's XML Console you can trace the communication between the client and the server:

Client: connecting...

<iq type="get" id="29">
  <query xmlns="jabber:iq:auth">
    <username>johndoe</username>
  </query>
</iq>

Server tells, which information the client should provide:

<iq type='result' id='29'>
  <query xmlns='jabber:iq:auth'>
    <username>johndoe</username>
    <digest/>
    <password/>
    <resource/>
  </query>
</iq>

Client fills in the blanks providing the needed information:

<iq type="set" id="30">
  <query xmlns="jabber:iq:auth">
    <username>johndoe</username>
    <digest>c26789d0exyz4adf8c61e62e8fef27e6d0de</digest>
    <resource>Gajim</resource>
  </query>
</iq>

Server: connection succeeded:

My server is a jabberd14 (the original reference implementation of the Jabber protocol), easily installable on Ubuntu by sudo aptitude install jabber.

As seen in the log, it requires the resource-clause - some sort of subnamespace. Gajim and Pidgin and probably all other Clients are smart enough to provide it if requested by the server. xmpp4r-simple is not.

The solution

The solution was to provide the required information from the beginning on, because xmpp4r ignores the Jabber typical handshake. Please note the /Home after the jabber id.

im = Jabber::Simple.new("[email protected]/Home", "password")
im.deliver("[email protected]", "Hey there friend!")

Read more about the Jabber communication protocoll.

Posted by VladimirDobriakov at 11:20 PM | Comments (0) | TrackBack