Class: Hirb::Formatter

Inherits:
Object
  • Object
show all
Defined in:
lib/hirb/formatter.rb

Overview

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

Instance Method Summary collapse

Constructor Details

#initialize(additional_config = {}) ⇒ Formatter

Returns a new instance of Formatter.



87
88
89
90
# File 'lib/hirb/formatter.rb', line 87

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

Instance Method Details

#call_output_method(output_method, output) ⇒ Object



168
169
170
# File 'lib/hirb/formatter.rb', line 168

def call_output_method(output_method, output)
  output_method.is_a?(Proc) ? output_method.call(output) : output.send(output_method)
end

#configObject

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.

:output_method

Specifies a method or proc to call on output before passing it to a helper. If the output is an array, it’s applied to every element in the array.

:options

Options to pass the helper method or class.

:ancestor

Boolean which when true causes subclasses of the output class to inherit its config. This doesn’t effect the current output class. Defaults to false. This is used by ActiveRecord classes.

Examples:
  {'WWW::Delicious::Element'=>{:class=>'Hirb::Helpers::ObjectTable', :ancestor=>true, :options=>{:max_width=>180}}}
  {'Date'=>{:class=>:auto_table, :ancestor=>true}}


106
107
108
# File 'lib/hirb/formatter.rb', line 106

def config
  @config
end

#default_configObject



187
188
189
190
191
192
193
194
195
196
# File 'lib/hirb/formatter.rb', line 187

def default_config
  Views.constants.inject({}) {|h,e|
    output_class = e.to_s.gsub("_", "::")
    if (views_class = Views.const_get(e)) && views_class.respond_to?(:render)
      default_options = views_class.respond_to?(:default_options) ? views_class.default_options : {}
      h[output_class] = default_options.merge({:class=>"Hirb::Views::#{e}"})
    end
    h
  }
end

#determine_helper_class(klass) ⇒ Object



153
154
155
156
157
158
# File 'lib/hirb/formatter.rb', line 153

def determine_helper_class(klass)
  if klass.is_a?(Symbol) && (helper_class = Helpers.constants.find {|e| e == Util.camelize(klass.to_s)})
    klass = "Hirb::Helpers::#{helper_class}"
  end
  Util.any_const_get(klass)
end

#determine_output_class(output) ⇒ Object



160
161
162
163
164
165
166
# File 'lib/hirb/formatter.rb', line 160

def determine_output_class(output)
  if output.is_a?(Array)
    output[0].class
  else
    output.class
  end
end

#format_class(klass, helper_config) ⇒ Object

Sets the helper config for the given output class.



111
112
113
114
115
# File 'lib/hirb/formatter.rb', line 111

def format_class(klass, helper_config)
  @klass_config.delete(klass)
  @config[klass.to_s] = helper_config
  true
end

#format_output(output, options = {}, &block) ⇒ Object

This is the main method of this class. The formatter looks for the first helper in its config for the given output class. If a helper is found, the output is converted by the helper into a string and returned. If not, nil is returned. The options this class takes are a helper config hash as described in config. These options will be merged with any existing helper config hash an output class has in config. Any block given is passed along to a helper class.



126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
# File 'lib/hirb/formatter.rb', line 126

def format_output(output, options={}, &block)
  output_class = determine_output_class(output)
  options = parse_console_options(options) if options.delete(:console)
  options = Util.recursive_hash_merge(klass_config(output_class), options)
  output = options[:output_method] ? (output.is_a?(Array) ? output.map {|e| call_output_method(options[:output_method], e) } : 
    call_output_method(options[:output_method], output) ) : output
  args = [output]
  args << options[:options] if options[:options] && !options[:options].empty?
  if options[:method]
    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

#klass_config(output_class) ⇒ Object

Internal view options built from user-defined ones. Options are built by recursively merging options from oldest ancestors to the most recent ones.



174
175
176
177
178
179
180
181
# File 'lib/hirb/formatter.rb', line 174

def klass_config(output_class)
  @klass_config[output_class] ||= begin
    output_ancestors_with_config = output_class.ancestors.map {|e| e.to_s}.select {|e| @config.has_key?(e)}
    @klass_config[output_class] = output_ancestors_with_config.reverse.inject({}) {|h, klass|
      (klass == output_class.to_s || @config[klass][:ancestor]) ? Util.recursive_hash_merge(h, @config[klass]) : h
    }
  end
end

#parse_console_options(options) ⇒ Object

:stopdoc:



145
146
147
148
149
150
151
# File 'lib/hirb/formatter.rb', line 145

def parse_console_options(options) #:nodoc:
  real_options = [:method, :class, :output_method].inject({}) do |h, e|
    h[e] = options.delete(e) if options[e]; h
  end
  real_options.merge! :options=>options
  real_options
end

#reloadObject

Reloads autodetected Hirb::Views



118
119
120
# File 'lib/hirb/formatter.rb', line 118

def reload
  @config = Util.recursive_hash_merge default_config, @config
end

#reset_klass_configObject



183
184
185
# File 'lib/hirb/formatter.rb', line 183

def reset_klass_config
  @klass_config = {}
end