Tagged With   gem:name=boson , lib:name=irb , post:lang=ruby , post:type=tutorial

Ruby Reference Commands With Boson

Continuing where this post left off, we’ll look at more examples of the unique command and view framework interaction that Boson and Hirb have. In particular, we’ll look at commands that are useful references to your current Ruby environment. Since Boson’s commands are just methods in a module, any of these commands work fine in irb without Boson.

Game Plan

We’ll be looking at my ruby_ref library and commands that reference:

For all these commands, I will only show the original ruby method. To see the Boson configuration for each, see the library source.

To follow along with the examples using boson:

  $ gem install boson boson-more
  $ echo "require 'boson/more'" >> ~/.bosonrc
  $ boson install https://github.com/cldwalker/irbfiles/raw/master/boson/commands/public/ruby_ref.rb

Global Variables

As we all know, Ruby comes with predefined global variables. Using the Kernel method global_variables to retrieve them, here’s a simple method to list global variables and their values:

  def global_var
    global_variables.sort.map {|e| [e, (eval e).inspect] }
  end

When Boson commandifies this method here’s what happens:

  bash> boson global_var
  +---------------------------+----------------------------------------------------------------------------------------------------+
  | variable                  | value                                                                                              |
  +---------------------------+----------------------------------------------------------------------------------------------------+
  | $!                        | nil                                                                                                |
  | $"                        | ["rubygems/defaults.rb", "thread.bundle", "thread.rb", "etc.bundle", "rbconfig.rb", "rubygems/e... |
  | $$                        | 33292                                                                                              |
  | $&                        | nil                                                                                                |
  | $'                        | nil                                                                                                |
  | $*                        | []                                                                                                 |
  # ...
  48 rows in set

  # Alphabetical sort on the value column
  bash> boson global_var '-s=val'   # global_var '--sort=value'
  +---------------------------+----------------------------------------------------------------------------------------------------+
  | variable                  | value                                                                                              |
  +---------------------------+----------------------------------------------------------------------------------------------------+
  | $FILENAME                 | "-"                                                                                                |
  | $0                        | "/usr/bin/boson"                                                                                   |
  | $PROGRAM_NAME             | "/usr/bin/boson"                                                                                   |
  | $-K                       | "UTF8"                                                                                             |
  | $KCODE                    | "UTF8"                                                                                             |
  | $/                        | "\n"                                                                                               |
  | $-0                       | "\n"                                                                                               |  
  # ...
  48 rows in set

  # Boson has a new global command option --query which allows us to search inside of a table's fields
  bash> boson global_var '-q=var:load'   # global_var '--query=variable:load'
  +------------------+-------------------------------------------------------------------------------------------------------------+
  | variable         | value                                                                                                       |
  +------------------+-------------------------------------------------------------------------------------------------------------+
  | $LOADED_FEATURES | ["rubygems/defaults.rb", "thread.bundle", "thread.rb", "etc.bundle", "rbconfig.rb", "rubygems/exceptions... |
  | $LOAD_PATH       | ["/Users/bozo/code/gems/bond/lib", "/Users/bozo/code/gems/boson/lib", "/Users/bozo/code/gems/alias/lib",... |
  +------------------+-------------------------------------------------------------------------------------------------------------+
  2 rows in set

  # If you're new to Boson, we can do any of the above from irb as well
  bash> irb
  >> global_var '-q=var:load'   # global_var '--query=variable:load'
  +------------------+-------------------------------------------------------------------------------------------------------------+
  | variable         | value                                                                                                       |
  +------------------+-------------------------------------------------------------------------------------------------------------+
  | $LOADED_FEATURES | ["rubygems/defaults.rb", "thread.bundle", "thread.rb", "etc.bundle", "rbconfig.rb", "rubygems/exceptions... |
  | $LOAD_PATH       | ["/Users/bozo/code/gems/bond/lib", "/Users/bozo/code/gems/boson/lib", "/Users/bozo/code/gems/alias/lib",... |
  +------------------+-------------------------------------------------------------------------------------------------------------+
  2 rows in set
  => true

As you can see, Boson took a simple one-line method and made it a useful command that searches, sorts and displays global variables and their values.

Loaded Paths

$LOADED_FEATURES ($") and $LOAD_PATH ($:) are handy global variables that give us required paths and the directories we can require from. Although we don’t know the full paths to the required files, it’s easy to calculate them with these global variables:

  def loaded_paths
    hash = {}
    $".each {|file|
      $:.each {|dir|
        if test(?e, File.join(dir, file))
          hash[file] = File.join(dir, file)
          break
        end
      }
    }
    hash
  end

This method gives us a hash mapping the required paths to their full paths. Let’s see what Boson does with this:

  >> loaded_paths '-s=req'  # loaded_paths '--sort=require_path'
  +----------------------------------------------------+---------------------------------------------------------------------------+
  | require_path                                       | full_path                                                                 |
  +----------------------------------------------------+---------------------------------------------------------------------------+
  | alias.rb                                           | /Users/bozo/code/gems/alias/lib/alias.rb                                  |
  | alias/console.rb                                   | /Users/bozo/code/gems/alias/lib/alias/console.rb                          |
  | alias/creator.rb                                   | /Users/bozo/code/gems/alias/lib/alias/creator.rb                          |
  | alias/creators/any_to_instance_method_creator.rb   | /Users/bozo/code/gems/alias/lib/alias/creators/any_to_instance_method_... |
  | alias/creators/class_method_creator.rb             | /Users/bozo/code/gems/alias/lib/alias/creators/class_method_creator.rb    |
  | alias/creators/class_to_instance_method_creator.rb | /Users/bozo/code/gems/alias/lib/alias/creators/class_to_instance_metho... |
  | alias/creators/constant_creator.rb                 | /Users/bozo/code/gems/alias/lib/alias/creators/constant_creator.rb        |
  | alias/creators/instance_method_creator.rb          | /Users/bozo/code/gems/alias/lib/alias/creators/instance_method_creator.rb |
  | alias/manager.rb                                   | /Users/bozo/code/gems/alias/lib/alias/manager.rb                          |
  # ...
  128 rows in set
  => true

  >> loaded_paths '-q=req:bundle   # loaded_paths '--query=require_path:bundle'
  +-----------------------------+---------------------------------------------------------------------------------------------------------------+
  | require_path                | full_path                                                                                                     |
  +-----------------------------+---------------------------------------------------------------------------------------------------------------+
  | digest.bundle               | /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/universal-darwin9.0/digest.bundle     |
  | digest/md5.bundle           | /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/universal-darwin9.0/digest/md5.bundle |
  | etc.bundle                  | /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/universal-darwin9.0/etc.bundle        |
  | readline.bundle             | /Library/Ruby/Site/1.8/universal-darwin9.0/readline.bundle                                                    |
  | readline_line_buffer.bundle | /Users/bozo/code/gems/bond/lib/readline_line_buffer.bundle                                                    |
  | stringio.bundle             | /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/universal-darwin9.0/stringio.bundle   |
  | syck.bundle                 | /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/universal-darwin9.0/syck.bundle       |
  | thread.bundle               | /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/universal-darwin9.0/thread.bundle     |
  +-----------------------------+---------------------------------------------------------------------------------------------------------------+
  8 rows in set
  => true

  # We can always toggle rendering on a command to get the method's return value with --render
  >> load_path('--render').values.slice(0,5)
  => ["/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/universal-darwin9.0/syck.bundle",
   "/Users/bozo/code/gems/bond/lib/bond/missions/default_mission.rb", "/Users/bozo/code/gems/boson/lib/boson/libraries/file_library.rb",
   "/Users/bozo/code/gems/hirb/lib/hirb/menu.rb", "/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/ostruct.rb"]

Gem Versions

For any number of reasons, it can be handy to know the versions of the gems you’re using. Since most gems don’t come with a VERSION constant, the easiest way to get their version is from their loaded gemspec. But since not everything we use nowadays comes as a gem i.e. rip packages, we could also extract the versions from $LOAD_PATH. Here’s a command that can do either one of these depending on the presence of a :loaded_path option:

  def gem_versions(options={})
    if options[:loaded_path]
      $:.map {|e| e =~ /\/([\w-]+)-(\d\.\d(\.\d)?)\/lib/ ? [$1,$2] : nil }.compact.uniq
    else
      Gem.loaded_specs.values.map {|e| [e.name, e.version] }
    end
  end

Let’s see it in action:

  # Using gemspecs to get the versions
  >> gem_versions
  +-----------------------+---------+
  | name                  | version |
  +-----------------------+---------+
  | matthew-method_lister | 0.2.3   |
  | mynyml-every          | 0.6     |
  | local_gem             | 0.1.1   |
  | duration              | 0.1.0   |
  +-----------------------+---------+
  4 rows in set
  => true

  # Using $: to get the versions
  >> gem_versions '-l'  # gem_versions '--loaded_path'  or gem_versions :loaded_path=>true
  +-----------------------+---------+
  | name                  | version |
  +-----------------------+---------+
  | local_gem             | 0.1.1   |
  | matthew-method_lister | 0.2.3   |
  | duration              | 0.1.0   |
  | mynyml-every          | 0.6     |
  +-----------------------+---------+
  4 rows in set
  => true

  # How does this command look inside a Rails' irb session?
  >> gem_versions '-s=n'   # gem_versions '--sort=name'
  +----------------------------+---------+
  | name                       | version |
  +----------------------------+---------+
  | RedCloth                   | 4.2.1   |
  | actionmailer               | 2.2.2   |
  | actionpack                 | 2.2.2   |
  | activerecord               | 2.2.2   |
  | activeresource             | 2.2.2   |
  | activesupport              | 2.2.2   |
  | alias                      | 0.2.1   |
  | cldwalker-has_machine_tags | 0.1.3   |
  | duration                   | 0.1.0   |
  | hirb                       | 0.2.7   |
  | local_gem                  | 0.1.1   |
  | matthew-method_lister      | 0.2.3   |
  | mynyml-every               | 0.6     |
  | rails                      | 2.2.2   |
  | rake                       | 0.8.7   |
  +----------------------------+---------+
  15 rows in set
  => true

Object Instance Variables

Whether you’re getting acquainted with a new class of objects or just trying to figure an object’s current state, looking at an object’s instance variables can be useful. Here’s a command that gives us any object’s instance variables and their values:

  def instance_var(obj)
    obj.instance_variables.map {|e| [e, obj.instance_variable_get(e)] }
  end

Using this with Boson:

  # Let's start with a Boson::Command object
  >> instance_var Boson.commands[0]
  +-------------------+-------------------------------+
  | instance          | value                         |
  +-------------------+-------------------------------+
  | @lib              | "core"                        |
  | @name             | "usage"                       |
  | @options          | {[:verbose, :V]=>:boolean}    |
  | @args             | [["name"], ["options", "{}"]] |
  | @file_parsed_args | true                          |
  | @description      | "Print a command's usage"     |
  +-------------------+-------------------------------+
  6 rows in set
  => true

  # How about the bigger Gem::Specification object?
  >> instance_var Gem.loaded_specs.values[0], '-s=i'  #  or '--sort=instance'
  +----------------------------+-----------------------------------------------------------------------------------------------------------------------+
  | instance                   | value                                                                                                                 |
  +----------------------------+-----------------------------------------------------------------------------------------------------------------------+
  | @authors                   | ["Matthew O'Connor"]                                                                                                  |
  | @autorequire               | nil                                                                                                                   |
  | @bindir                    | "bin"                                                                                                                 |
  | @cert_chain                | []                                                                                                                    |
  | @date                      | Mon Oct 27 00:00:00 -0400 2008                                                                                        |
  | @default_executable        | nil                                                                                                                   |
  | @dependencies              | []                                                                                                                    |
  | @description               | nil                                                                                                                   |
  | @email                     | "matthew @[email protected] canonical.org"                                                                                      |
  | @executables               | []                                                                                                                    |
  | @extensions                | []                                                                                                                    |
  | @extra_rdoc_files          | ["README.markdown"]                                                                                                   |
  | @files                     | ["lib/method_lister/color_display.rb", "lib/method_lister/find_result.rb", "lib/method_lister/finder.rb", "lib/met... |
  | @has_rdoc                  | true                                                                                                                  |
  | @homepage                  | "http://github.com/matthew/method_lister/tree/master"                                                                 |
  | @licenses                  | []                                                                                                                    |
 # ...
 35 rows in set
 => true

 #  Search instances containing files
 >> instance_var Gem.loaded_specs.values[0], '-q=i:files'   #  or '--query=instance:files'
 +-------------------+--------------------------------------------------------------------------------------------------------------------------------+
 | instance          | value                                                                                                                          |
 +-------------------+--------------------------------------------------------------------------------------------------------------------------------+
 | @files            | ["lib/method_lister/color_display.rb", "lib/method_lister/find_result.rb", "lib/method_lister/finder.rb", "lib/method_liste... |
 | @test_files       | ["spec/color_display_spec.rb", "spec/find_result_spec.rb", "spec/finder_spec.rb", "spec/ruby_ext_spec.rb", "spec/simple_dis... |
 | @extra_rdoc_files | ["README.markdown"]                                                                                                            |
 +-------------------+--------------------------------------------------------------------------------------------------------------------------------+
 3 rows in set
 => true

 # So what if want to see everything inside the value field?
 >> instance_var Gem.loaded_specs.values[0], '-q=i:files -V'   # or '--query=instance:files --vertical'
 ************ 1. row ************
 instance: @files
    value: ["lib/method_lister/color_display.rb", "lib/method_lister/find_result.rb", "lib/method_lister/finder.rb",
  "lib/method_lister/ruby_ext.rb", "lib/method_lister/simple_display.rb", "lib/method_lister.rb", "spec/color_display_spec.rb",
  "spec/find_result_spec.rb", "spec/finder_spec.rb", "spec/helpers/matchers/list_methods.rb",
  "spec/helpers/object_mother/find_result.rb", "spec/helpers/object_mother/find_scenario.rb", "spec/rcov.opts", "spec/ruby_ext_spec.rb",
  "spec/scenarios/class_with_inheritance.rb", "spec/scenarios/class_with_inheritance_and_modules.rb", "spec/scenarios/eigenclass.rb",
  "spec/scenarios/eigenclass_with_modules.rb", "spec/scenarios/filters_results_without_methods.rb",
  "spec/scenarios/mixed_visibility_methods.rb", "spec/scenarios/object_without_eigenclass.rb", "spec/scenarios/overloaded_methods.rb"
  "spec/scenarios/overloaded_methods_with_modules_mixed_in.rb", "spec/scenarios/private_methods.rb", "spec/scenarios/single_class.rb",
  "spec/scenarios/single_class_with_module_mixed_in.rb", "spec/simple_display_spec.rb", "spec/spec.opts", "spec/spec_helper.rb",
   "README.markdown"]
 ************ 2. row ************
 instance: @test_files
    value: ["spec/color_display_spec.rb", "spec/find_result_spec.rb", "spec/finder_spec.rb", "spec/ruby_ext_spec.rb",
  "spec/simple_display_spec.rb"]
 ************ 3. row ************
 instance: @extra_rdoc_files
    value: ["README.markdown"]
 3 rows in set
 => true 

The pp method from the standard library is another good alternative for displaying an object’s instance variables. Personally, I find the fact that any class can customize their pp output to be a strength but also a weakness. The weakness lies in that formatting of objects become inconsistent across classes and there’s no guarantee that pp will give you all instance variables. Also, to get a good overview of an object I think it helps to stick to one instance variable per line.

RbConfig

If you have ever had to ensure a gem works across multiple platforms, you’ve probably encountered RbConfig. This module, which is generated by mkconfig.rb when your ruby is built, contains a hash constant, CONFIG, containing platform-specific configuration. So let’s make a command that simply points to this config hash:

  def rbconfig
    require 'rbconfig'
    RbConfig::CONFIG
  end

From the above, we already know we can display, search and sort data structures pretty easily. So let’s just focus on searching features that have been dormant til now:

  # We know we can search in a field
  bash> boson rbconfig -q=n:arch    # or --query=name:arch
  +-------------+---------------------------------------------------------------------------------------------+
  | name        | value                                                                                       |
  +-------------+---------------------------------------------------------------------------------------------+
  | archdir     | /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/universal-darwin9.0 |
  | sitearch    | universal-darwin9.0                                                                         |
  | arch        | universal-darwin9.0                                                                         |
  | sitearchdir | /Library/Ruby/Site/1.8/universal-darwin9.0                                                  |
  | ARCHFILE    |                                                                                             |
  | ARCH_FLAG   |                                                                                             |
  +-------------+---------------------------------------------------------------------------------------------+
  6 rows in set

  # But you can also search in all fields with '*'
  bash> boson rbconfig -q=*:arch    # or --query=name,value:arch
  +------------------+---------------------------------------------------------------------------------------------+
  | name             | value                                                                                       |
  +------------------+---------------------------------------------------------------------------------------------+
  | archdir          | /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/universal-darwin9.0 |
  | sitearch         | universal-darwin9.0                                                                         |
  | arch             | universal-darwin9.0                                                                         |
  | sitearchdir      | /Library/Ruby/Site/1.8/universal-darwin9.0                                                  |
  | ARCHFILE         |                                                                                             |
  | ARCH_FLAG        |                                                                                             |
  | LDSHARED         | cc -arch ppc -arch i386 -pipe -bundle -undefined dynamic_lookup                             |
  | LDFLAGS          | -L. -arch ppc -arch i386                                                                    |
  | LIBRUBY_LDSHARED | cc -arch ppc -arch i386 -pipe -dynamiclib                                                   |
  | CFLAGS           | -arch ppc -arch i386 -Os -pipe -fno-common                                                  |
  +------------------+---------------------------------------------------------------------------------------------+
  10 rows in set

  # Searches are regular expressions
  bash> boson rbconfig -q=n:ruby$   # or --query=name:ruby$
  +----------+--------------------------------------------------+
  | name     | value                                            |
  +----------+--------------------------------------------------+
  | LIBRUBY  | libruby.1.dylib                                  |
  | MINIRUBY | ./miniruby                                       |
  | RUNRUBY  | ./miniruby $(srcdir)/runruby.rb --extout=.ext -- |
  +----------+--------------------------------------------------+
  3 rows in set

  # Multiple searches can be joined together by a comma
  bash> boson rbconfig -q=n:ruby$,v:package  # or --query=name:ruby$,value:package
  +----------+---------------------------------------------------------------------------------+
  | name     | value                                                                           |
  +----------+---------------------------------------------------------------------------------+
  | LIBRUBY  | libruby.1.dylib                                                                 |
  | MINIRUBY | ./miniruby                                                                      |
  | RUNRUBY  | ./miniruby $(srcdir)/runruby.rb --extout=.ext --                                |
  | docdir   | /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/share/doc/$(PACKAGE) |
  | dvidir   | /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/share/doc/$(PACKAGE) |
  | psdir    | /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/share/doc/$(PACKAGE) |
  | htmldir  | /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/share/doc/$(PACKAGE) |
  | pdfdir   | /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/share/doc/$(PACKAGE) |
  +----------+---------------------------------------------------------------------------------+
  8 rows in set

Game Over

As you can see, Boson does a decent job of turning simple methods into useful commands. In these examples, we saw the return values of these commands converted into well-formatted, sortable and searchable tables without adding any code. If you look at the library source you will see commented render_options calls for each command. These are what hooked into Boson to give us all this functionality. To understand how this all works and create your own similar commands, check out the Boson docs and the Hirb table docs. If you want to check out more libraries like this, there are plenty. If you have ruby reference commands/methods, feel free to share them below.

Enjoyed this post? Tell others! hacker newsHacker News | twitterTwitter | DeliciousDelicious | redditReddit
blog comments powered by Disqus