Skip to content

Commit

Permalink
more view api additions, updated changelog + rdoc
Browse files Browse the repository at this point in the history
  • Loading branch information
cldwalker committed Jun 19, 2009
1 parent 0345455 commit 873b1c6
Show file tree
Hide file tree
Showing 6 changed files with 132 additions and 113 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.rdoc
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
== 0.2.0
* Major refactoring with bug fixes and better tests.
* Improved table algorithm to ensure that tables don't wrap.
* Added a pager which detects if output should be paged, Hirb::Pager.
* Added a selection menu, Hirb::Menu
* Following API changes: Hirb::Helpers::Table.max_width removed and config files don't use
the :view key anymore.
== 0.1.2
* Added tree views.
* Added output_method option to Hirb::View.render_output.
Expand Down
127 changes: 24 additions & 103 deletions README.rdoc
Original file line number Diff line number Diff line change
@@ -1,31 +1,36 @@
== Description

Hirb currently provides a mini view framework for console applications, designed with irb in mind.
Given the output of a console application, it renders a view if there is one configured, based on
the output's class. The framework encourages reusing views by letting you package them in classes
and associate them with any number of output classes. Hirb comes with tree views (see
Hirb currently provides a mini view framework for console applications, designed to improve irb's default output.
Hirb improves console output by providing a smart pager and auto-formatting output. The smart pager detects when an output exceeds
a screenful and thus only pages output as needed. Auto-formatting adds a view to an output's class. This is helpful in separating
views from content (MVC anyone?). The framework encourages reusing views by letting you
package them in classes and associate them with any number of output classes. Hirb comes with tree views (see
Hirb::Helpers::Tree) and table views (see Hirb::Helpers::Table). By default Hirb displays Rails'
model classes as tables.
model classes as tables. Hirb also sports a nice selection menu, Hirb::Menu.

== Install

Install the gem with:

sudo gem install cldwalker-hirb --source http://gems.github.com

== Create and Configure Views

If you'd like to learn how to create and configure views, {read the docs}[http://tagaholic.me/hirb/doc/classes/Hirb/Formatter.html].

== Rails Example

Let's load and enable the view framework:
bash> script/console
Loading local environment (Rails 2.2.2)
irb>> require 'hirb'
=> true
irb>> Hirb::View.enable
irb>> Hirb.enable
=> nil

The default configuration provides table views for ActiveRecord::Base descendants.
If a class isn't configured, Hirb reverts to irb's default echo mode.
irb>> Hirb::View.output_config
irb>> Hirb::View.formatter_config
=> {"ActiveRecord::Base"=>{:class=>"Hirb::Views::ActiveRecord_Base", :ancestor=>true}}

# Tag is a model class and descendant of ActiveRecord::Base
Expand Down Expand Up @@ -60,7 +65,7 @@ you may appreciate it also detects configured output objects in an array:
3 rows in set

At any time you can disable Hirb if you really like irb's lovely echo mode:
irb>> Hirb::View.disable
irb>> Hirb.disable
=> nil
irb>> Tag.all :limit=>3, :order=>"id DESC"
=> [#<Tag id: 907, name: "gem:tags=yaml", description: nil, created_at: "2009-03-06 21:10:41",
Expand All @@ -74,7 +79,7 @@ While preconfigured tables are great for database records, sometimes you just wa
tables/views for any output object:

#These examples don't need to have Hirb::View enabled.
irb>>Hirb::View.disable
irb>>Hirb.disable
=>nil

# Imports table() and view()
Expand All @@ -91,8 +96,8 @@ tables/views for any output object:
+------------+--------+-----------+-------+--------------------------+
2 rows in set

# Same table as the previous method. However view() will be able to call any view created.
irb>> view [Date.today, Date.today.next_month], :class=>Hirb::Helpers::ObjectTable,
# Same table as the previous method. However view() will be able to call any helper.
irb>> view [Date.today, Date.today.next_month], :class=>:object_table,
:fields=>[:to_s, :ld, :ajd, :amjd, :asctime]

If these console methods weren't convenient enough, try:
Expand All @@ -101,15 +106,15 @@ If these console methods weren't convenient enough, try:
irb>> require 'hirb/import_object'
=>true
# Yields same table as above examples.
irb>> [Date.today, Date.today.next_month].view :class=>Hirb::Helpers::ObjectTable,
irb>> [Date.today, Date.today.next_month].view :class=>:object_table,
:fields=>[:to_s, :ld, :ajd, :amjd, :asctime]

Although views by default are printed to STDOUT, they can be easily modified to write anywhere:
# Setup views to write to file 'console.log'.
irb>> Hirb::View.render_method = lambda {|output| File.open("console.log", 'w') {|f| f.write(output) } }

# Writes to file with same table output as above example.
irb>> view [Date.today, Date.today.next_month], :class=>Hirb::Helpers::ObjectTable,
irb>> view [Date.today, Date.today.next_month], :class=>:object_table,
:fields=>[:to_s, :ld, :ajd, :amjd, :asctime]

# Doesn't write to file because Symbol isn't configured to use Hirb::View and thus defaults to irb's echo mode.
Expand All @@ -119,98 +124,13 @@ Although views by default are printed to STDOUT, they can be easily modified to
# Go back to printing Hirb views to STDOUT.
irb>> Hirb::View.reset_render_method

== Create and Configure Views
Let's create a simple view and configure it in different ways to be Hash's default view:

=== Setup
irb>> require 'hirb'
=>true
irb>> Hirb::View.enable
=>nil
irb>> require 'yaml'
=>true

=== Configure As View Method
A view method is the smallest reuseable view.
# Create yaml view method
irb>> def yaml(output); output.to_yaml; end
=>nil

# Configure view and reload it
irb>>Hirb::View.output_config = {"Hash"=>{:method=>:yaml}}
=>{"Hash"=>{:method=>:yaml}}
irb>>Hirb::View.reload_config
=>true

# Hashes now appear as yaml
irb>>{:a=>1, :b=>{:c=>3}}
---
:a : 1
:b :
:c : 3
=> true

=== Configure As View Class
A view class is suited for more complex views. View classes can be under any namespace
and are expected to provide a render method. However, if a class is under the Hirb::Views namespace,
it will be automatically loaded with no configuration. Something to think about when
sharing views with others.

# Create yaml view class
irb>> class Hirb::Views::Hash; def self.render(output, options={}); output.to_yaml; end ;end
=>nil
# Just reload since no configuration is necessary
irb>>Hirb::View.reload_config

# Hashes now appear as yaml ...

Although the Hirb::Views namespace is great for quick classes that just plug and play, you
often want view classes that can be reused with multiple outputs. For this case, it's recommended to
use the Hirb::Helpers namespace.

# Create yaml view class
irb>> class Hirb::Helpers::Yaml; def self.render(output, options={}); output.to_yaml; end ;end
=>nil

# Configure view and reload it
irb>>Hirb::View.output_config = {"Hash"=>{:class=>"Hirb::Helpers::Yaml"}}
=>{"Hash"=>{:class=>"Hirb::Helpers::Yaml"}}
irb>>Hirb::View.reload_config

# Hashes now appear as yaml ...

=== Configure At Startup
Once you know what views are associated with what output classes, you can configure
them at startup by passing Hirb::View.enable a block:
# In .irbrc
require 'hirb'
# View class needs to come before enable()
class Hirb::Helpers::Yaml; def self.render(output, options={}); output.to_yaml; end ;end
Hirb::View.enable {|conf| conf.output = {"Hash"=>{:class=>"Hirb::Helpers::Yaml"}} }

Or by creating a config file at config/hirb.yml or ~/.hirb.yml:
# The config file for the yaml example would look like:
# ---
# :view :
# :output :
# Hash :
# :class : Hirb::Helpers::Yaml

# In .irbrc
require 'hirb'
# View class needs to come before enable()
class Hirb::Helpers::Yaml; def self.render(output, options={}); output.to_yaml; end ;end
Hirb::View.enable

== Contributing Views
If you have views of your own you'd like to share, fork Hirb and put your views under
the Hirb::Helpers namespace and the view files under lib/hirb/helpers/.
== Sharing Views
If you have tested views you'd like to share, fork Hirb and put your views under
the lib/hirb/views/ and/or helpers files under lib/hirb/helpers/. If not tested, feel free to share
them on the wiki.

== Limitations
Although Hirb preserves Wirble colorizing irb's default echo mode, it doesn't colorize its own views.
This is mainly because colorizing caused table classes to render incorrectly. If you can get tables
and colors to work nicely, please fork. To colorize your Hirb output:
Hirb::View.render_method = lambda {|output| puts Wirble::Colorize.colorize(output) }
If using Wirble, you should call Hirb after it since they both override irb's default output.

== Motivation
Table code from http://gist.github.com/72234 and {my console
Expand All @@ -225,5 +145,6 @@ Please report them {on github}[http://github.com/cldwalker/hirb/issues].

== Todo
* Consider applying multiple views/filters to output.
* Consider mapping a class' methods to their own views.
* Provides helper methods to all view classes.
* Consider adding a template system as needed.
2 changes: 2 additions & 0 deletions lib/hirb.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
require 'hirb/menu'

# Most of Hirb's functionality currently resides in Hirb::View.
# For an in-depth tutorial on creating and configuring views see Hirb::Formatter.
# Hirb has an optional yaml config file defined by config_file(). This config file
# has the following top level keys:
# [:output] This hash is used by the formatter object. See Hirb::Formatter.config for its format.
Expand All @@ -20,6 +21,7 @@
# [:pager] Boolean which determines if the pager is enabled. Defaults to true.
# [:pager_command] Command to be used for paging. Command can have options after it i.e. 'less -r'.
# Defaults to common pagers i.e. less and more if detected.
#

module Hirb
class <<self
Expand Down
92 changes: 88 additions & 4 deletions lib/hirb/formatter.rb
Original file line number Diff line number Diff line change
@@ -1,15 +1,96 @@
module Hirb
# This class is used by the View to format an output into a string. The formatter object looks for an output's class config
# in Hirb::Formatter.config and if found applies a helper to the output.
=begin rdoc
This class is format an output into a string using Hirb::Helpers::*, Hirb::Views::* or any user-created views.
The formatter object looks for an output's class config in Hirb::Formatter.config and if found applies a helper to the output.
== Create and Configure Views
Let's create a simple view and configure it in different ways to be Hash's default view:
=== Setup
irb>> require 'hirb'
=>true
irb>> Hirb.enable
=>nil
irb>> require 'yaml'
=>true
=== Configure As View Method
A view method is the smallest reuseable view.
# Create yaml view method
irb>> def yaml(output); output.to_yaml; end
=>nil
# Configure view
irb>>Hirb::View.format_class Hash, :method=>:yaml
=>true
# Hashes now appear as yaml
irb>>{:a=>1, :b=>{:c=>3}}
---
:a : 1
:b :
:c : 3
=> true
=== Configure As View Class
A view class is suited for more complex views. View classes can be under any namespace
and are expected to provide a render method. However, if a class is under the Hirb::Views namespace,
it will be automatically loaded with no configuration. Something to think about when
sharing views with others.
# Create yaml view class
irb>> class Hirb::Views::Hash; def self.render(output, options={}); output.to_yaml; end ;end
=>nil
# Just reload since no configuration is necessary
irb>>Hirb::View.formatter.reload
# Hashes now appear as yaml ...
Although the Hirb::Views namespace is great for quick classes that just plug and play, you
often want view classes that can be reused with multiple outputs. For this case, it's recommended to
use the Hirb::Helpers namespace.
# Create yaml view class
irb>> class Hirb::Helpers::Yaml; def self.render(output, options={}); output.to_yaml; end ;end
=>nil
# Configure view and reload it
irb>>Hirb::View.format_class Hash, :class=>"Hirb::Helpers::Yaml"
=>true
# Hashes now appear as yaml ...
== Configure At Startup
Once you know what views are associated with what output classes, you can configure
them at startup by passing Hirb.enable an options hash:
# In .irbrc
require 'hirb'
# View class needs to come before enable()
class Hirb::Helpers::Yaml; def self.render(output, options={}); output.to_yaml; end ;end
Hirb.enable :output=>{"Hash"=>{:class=>"Hirb::Helpers::Yaml"}}
Or by creating a config file at config/hirb.yml or ~/.hirb.yml:
# The config file for the yaml example would look like:
# ---
# :output :
# Hash :
# :class : Hirb::Helpers::Yaml
# In .irbrc
require 'hirb'
# View class needs to come before enable()
class Hirb::Helpers::Yaml; def self.render(output, options={}); output.to_yaml; end ;end
Hirb.enable
=end

class Formatter
def initialize(additional_config={})
@klass_config = {}
@config = Util.recursive_hash_merge default_config, additional_config || {}
end

# A hash of Ruby class strings mapped to helper config hashes. A helper config hash must have at least a :method or :class option
# for a helper to be applied to an output. A helper config hash has the following keys:
# A hash of Ruby class strings mapped to helper config hashes. A helper config hash must have at least a :method, :output_method
# or :class option for a helper to be applied to an output. A helper config hash has the following keys:
# [:method] Specifies a global (Kernel) method to do the formatting.
# [:class] Specifies a class to do the formatting, using its render() class method. If a symbol it's converted to a corresponding
# Hirb::Helpers::* class if it exists.
Expand All @@ -26,6 +107,7 @@ def config
@config
end

# Sets the helper config for the given output class.
def format_class(klass, helper_config)
@klass_config.delete(klass)
@config[klass.to_s] = helper_config
Expand All @@ -52,6 +134,8 @@ def format_output(output, options={}, &block)
new_output = send(options[:method],*args)
elsif options[:class] && (helper_class = determine_helper_class(options[:class]))
new_output = helper_class.render(*args, &block)
elsif options[:output_method]
new_output = output
end
new_output
end
Expand Down
15 changes: 10 additions & 5 deletions lib/hirb/view.rb
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,16 @@ def height
config ? config[:height] : DEFAULT_HEIGHT
end

# Current formatter config
def formatter_config
formatter.config
end

# Sets the helper config for the given output class.
def format_class(klass, helper_config)
formatter.format_class(klass, helper_config)
end

#:stopdoc:
def render_output(output, options={})
if (formatted_output = formatter.format_output(output, options))
Expand Down Expand Up @@ -132,11 +142,6 @@ def pager

def pager=(value); @pager = value; end

# Config hash which maps classes to view hashes. View hashes are the same as the options hash of render_output().
def formatter_config
formatter.config
end

def formatter(reload=false)
@formatter = reload || @formatter.nil? ? Formatter.new(config[:output]) : @formatter
end
Expand Down
2 changes: 1 addition & 1 deletion test/formatter_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ def self.render(strings)
end

test "doesn't format and returns false when no format method found" do
Hirb.enable
Hirb.enable
render_method.expects(:call).never
view_output(Date.today).should == false
end
Expand Down

0 comments on commit 873b1c6

Please sign in to comment.