The Artima Developer Community
Sponsored Link

Ruby Buzz Forum
Page Irb Output And Improve Ri With Hirb

0 replies on 1 page.

Welcome Guest
  Sign In

Go back to the topic listing  Back to Topic List Click to reply to this topic  Reply to this Topic Click to search messages in this forum  Search Forum Click for a threaded view of the topic  Threaded View   
Previous Topic   Next Topic
Flat View: This topic has 0 replies on 1 page
Gabriel Horner

Posts: 62
Nickname: cldwaker
Registered: Feb, 2009

Gabriel Horner is an independent consultant who can't get enough of Ruby
Page Irb Output And Improve Ri With Hirb Posted: Jun 19, 2009 5:52 PM
Reply to this message Reply

This post originated from an RSS feed registered with Ruby Buzz by Gabriel Horner.
Original Post: Page Irb Output And Improve Ri With Hirb
Feed Title: Tagaholic
Feed URL: http://feeds2.feedburner.com/tagaholic
Feed Description: My ruby/rails/knowledge management thoughts
Latest Ruby Buzz Posts
Latest Ruby Buzz Posts by Gabriel Horner
Latest Posts From Tagaholic

Advertisement
Hirb just got two sweet features with the 0.2 release – a pager and a selection menu. With the pager, you’ll never have to scroll because irb dumped an ungodly inspect() on you. With the selection menu, you’ll have RI taking useful method dumps in no time. To play with what follows, you’ll need the latest Hirb:
  gem install cldwalker-hirb --source http://gems.github.com

Paging With Irb

If you’re unsure that irb’s output can be quite horrendous at times:
  bash> irb -rubygems
  >> Gem.source_index
  # Human sacrifice, dogs and cats living together ... mass hysteria!
Sure, we can turn it off:
  >> conf.echo = false
  >> Gem.source_index
  >>
  # Sorry to disappoint you Dr. Venkman

But sometimes we want to see some of the output, we want to explore an API, we want to sift through an object’s guts and find something useful. But mostly, we just don’t want to be crapped on for executing our half-baked crap … I mean code. Enter the pager:

  bash> irb -rubygems -rhirb
  >> Hirb.enable
  => nil
  >> Gem.source_index
  => # Just a screenful of output here. Cheers and hip hoorays ensue.

Is that it? That’s it. Hirb by default has paging turned on. You can specify your own pager command or have it detect your system’s pager i.e. ENV['PAGER'], less or more. If no system pager is detected it defaults to a very basic ruby pager: you page or you quit.

So you may be wondering, is every irb output paged? Hell no. Hirb is aware of your console’s width and height and uses that to only page output that exceeds a screenful. If you’re a nixer, Hirb should be able to detect your width and height:

  bash> irb -rubygems -rhirb
  >> Hirb.enable
  => nil
  >> Hirb::View.width
  => 208
  >> Hirb::View.height
  => 51

  # Sweet, hirb detected my console size.
  # I can take advantage of this auto-detection i.e. after changing my console window's size.
  >> Hirb::View.resize
  => [172, 45]

If autodetection doesn’t work, it’s still pretty easy:

  bash> irb -rubygems -rhirb
  # Let's determine our width.
  # Use the line that starts wrapping to give you an approximate width.
  >> 80.step(200, 10) {|e| puts e.to_s + "*" * (e - e.to_s.size) ; sleep 0.2 }
  80******************************************************************************
  90****************************************************************************************

  # Let's do something similar for height.
  # Stop it when two numbers aren't on the same screen anymore
  # 25.step(80, 5) {|e| puts "*\n" * (e - 1) + e.to_s ; sleep 1.5 }

  >> Hirb.enable :width=>your_width, :height=>your_height
  => nil

  # If you change your console window, just explicitly pass your width and height:
  >> Hirb::View.resize(120, 30)

Improving RI With Menus

RI is pretty handy as far as keystrokes to documentation goes. But it can be improved.

Before improving it, let’s look at the selection menu that comes with Hirb:

  bash> irb -rubygems -rhirb
  >> extend Hirb::Console
  => main
  >> menu ('a'..'h').to_a
  +--------+-------+
  | number | value |
  +--------+-------+
  | 1      | a     |
  | 2      | b     |
  | 3      | c     |
  | 4      | d     |
  | 5      | e     |
  | 6      | f     |
  | 7      | g     |
  | 8      | h     |
  +--------+-------+
  8 rows in set
  Choose : # Here's where you type
  # Typing: 1-3,6-7  => ["a", "b", "c", "f", "g"]
  # Typing: 1..3,6..7  => ["a", "b", "c", "f", "g"]
  # Typing: * => ["a", "b", "c", "d", "e", "f", "g", "h"]
  # Typing: enter/return => []

As you can see, menu() uses Hirb’s tables to display the array. You can select a subset by specifying ranges with - and separating multiple ranges with ,. If you’re feeling rubyish you can use .. for ranges i.e. 1..3, 6..7. So what’s the big deal? Ruby’s Array.slice() can do most of this already. Well, let’s go back to rdoc’s ri.

You may or may not be familiar with ri’s interactive mode, ri -i. Basically it allows you to autocomplete methods as you would in irb. This ri feature seems to have been started by Daniel Choi. As part of his patch he had a basic selection menu for resolving multiple matches on a query. Let’s see if we can continue where Daniel left off using Hirb’s menu().

First we need to figure out how to query ri and get back a list of matches. If you look at the ri command, you see that ri is run by the class RDoc::RI::Driver. So if we create an RDoc::RI::Driver object, how can we select a list of matches? If you look around, you’ll figure out select_methods() is what we want:

  bash> irb -rubygems
  >> require 'rdoc/ri/driver'
  => true
  => conf.echo = false
  >> driver = RDoc::RI::Driver.new(RDoc::RI::Driver.process_args(['set_trace']))
  >> matches = driver.select_methods(/set_trace/)
  >> conf.echo = true
  >> matches.class
  => Array
  >> matches[0]
  => {"visibility"=>"public", "name"=>"set_trace", "is_singleton"=>true, "params"=>"( arg )", "block_params"=>nil, "aliases"=>[],
   "full_name"=>"DEBUGGER__::set_trace", "comment"=>nil, "source_path"=>"Ruby 1.8"}

   # Let's pass a specific match back to ri to get it's documentation
   >> RDoc::RI::Driver.run [ matches[0]['full_name'] ]
   -------------------------------------------------- DEBUGGER__::set_trace
        DEBUGGER__::set_trace( arg )

        From Ruby 1.8
   ------------------------------------------------------------------------
        [no description]
   => ["DEBUGGER__::set_trace"]

Knowing how to extract an ri query result and feed it back to ri, let’s use a Hirb menu with its block syntax which passes the chosen items to the block:

  >> require 'hirb'; extend Hirb::Console
  => main
  >> menu(matches, :fields=>['full_name']) {|e| RDoc::RI::Driver.run(e.map {|e| e['full_name']}) }
  +--------+-----------------------------------+
  | number | full_name                         |
  +--------+-----------------------------------+
  | 1      | DEBUGGER__::set_trace             |
  | 2      | DEBUGGER__::Context#set_trace     |
  | 3      | DEBUGGER__::Context#set_trace_all |
  | 4      | Kernel#set_trace_func             |
  +--------+-----------------------------------+
  4 rows in set
  Choose : 1-3 # You type 1-3
  # Prints the ri documentation for the first 3 methods

All of a sudden we have the choice to see the documentation of *any method(s)! Simply type a number or a range of numbers. What if we could apply this to a Ruby class i.e. list a class’ methods and choose from them?:

  def ri(original_query, regex=nil)
    query = original_query.to_s
    ri_driver = RDoc::RI::Driver.new(RDoc::RI::Driver.process_args([query]))

    # if query is a class ri recognizes
    if (class_cache = ri_driver.class_cache[query])
      methods = []
      class_methods = class_cache["class_methods"].map {|e| e["name"]}
      instance_methods = class_cache["instance_methods"].map {|e| e["name"]}
      if regex
        class_methods = class_methods.grep(/#{regex}/)
        instance_methods = instance_methods.grep(/#{regex}/)
      end  
      all_methods = class_methods.each {|e| methods << {:name=>"#{query}.#{e}", :type=>:class}} +
        instance_methods.each {|e| methods << {:name=>"#{query}.#{e}", :type=>:instance}}
      menu(methods, :fields=>[:name, :type]) do |chosen|
        system_ri(*chosen.map {|e| e[:name]})
      end
    else
      results = ri_driver.select_methods(/#{query}/)
      menu(results, :fields=>['full_name'], :ask=>false) do |chosen|
        system_ri(*chosen.map {|e| e['full_name']})
      end    
    end
  end

  def system_ri(*queries)
    ::Hirb::View.capture_and_render { RDoc::RI::Driver.run(queries) }
  end

I’ll let you figure out how the above works. Let’s just use it:

  # Wouldn't it be nice to page as above
  >> Hirb.enable
  => nil
  >> ri Array
  +--------+--------------------------+----------+
  | number | name                     | type     |
  +--------+--------------------------+----------+
  | 1      | Array.[]                 | class    |
  | 2      | Array.new                | class    |
  | 3      | Array.&                  | instance |
  | 4      | Array.*                  | instance |
  | 5      | Array.+                  | instance |
  | 6      | Array.-                  | instance |
  | 7      | Array.<<                 | instance |
  | 8      | Array.<=>                | instance |
  | 9      | Array.==                 | instance |
  | 10     | Array.[]                 | instance |
  # omitting the other 71 rows for brevity
  Choose: 1-10
  # Out comes the documentation for the 2 class methods and the first 8 instance methods.

  # If we want to filter a subset of a class, just pass a second argument to be interpreted as a regex.
  # Any methods containing 'to'
  >> ri Array, :to
  +--------+---------------+----------+
  | number | name          | type     |
  +--------+---------------+----------+
  | 1      | Array.to_a    | instance |
  | 2      | Array.to_ary  | instance |
  | 3      | Array.to_s    | instance |
  | 4      | Array.to_yaml | instance |
  +--------+---------------+----------+
  Choose:

In the first example above, paging works not only for the menu but also for all the ri output. If you take a look at the above ri() method, you’ll see that it wrap ri’s output with a Hirb::View.capture_and_render block. As you may have guessed, that method capture’s ri’s STDOUT and run’s it through Hirb’s pager.

If you’re curious, the actual ri library I use in irb is here. It covers some more ri edge cases I didn’t go into here. That library could easily be made into a command if you want. To use it in irb, drop this in your irbrc:
  require 'hirb'
  Hirb.enable
  extend Hirb::Console
  load "/path/to/ri_library" 
  class << self; include Ri; end

Wrapping Up With A Wrap Challenge

In wrapping this up, I’d like to mention the hirb config file format has changed. Basically you don’t need to nest everything under the :view key. Also, I’d like to mention that this release has an improved algorithm for automatically rendering console tables that don’t wrap. So if you set your width and height correctly, as explained above, I challenge you to pass Hirb’s table() arrays that cause it to wrap. Well, arrays you actually want to use.

Read: Page Irb Output And Improve Ri With Hirb

Topic: Finite maps galore: imperative code strikes back Previous Topic   Next Topic Topic: ORD Sessions and and introducing Flurn

Sponsored Links



Google
  Web Artima.com   

Copyright © 1996-2019 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use