lib/hirb/view.rb in cldwalker-hirb-0.1.2 vs lib/hirb/view.rb in cldwalker-hirb-0.2.0

- old
+ new

@@ -1,196 +1,172 @@ module Hirb - # This class contains one method, render_output, which formats and renders the output its given from a console application. - # However, this only happens for output classes that are configured to do so or if render_output is explicitly given - # a view formatter. The hash with the following keys are valid for Hirb::View.config as well as the :view key mentioned in Hirb: - # [:output] This hash is saved to output_config. It maps output classes to hashes that are passed to render_output. Thus these hashes - # take the same options as render_output. In addition it takes the following keys: - # * :ancestor- Boolean which if true allows all subclasses of the configured output class to inherit this config. - # - # Example: {'String'=>{:class=>'Hirb::Helpers::Table', :ancestor=>true, :options=>{:max_width=>180}}} + # This class is responsible for managing all view-related functionality. Its functionality is determined by setting up a configuration file + # as explained in Hirb and/or passed configuration directly to Hirb.enable. Most of the functionality in this class is dormant until enabled. module View + DEFAULT_WIDTH = 120 + DEFAULT_HEIGHT = 40 class<<self - attr_accessor :config, :render_method + attr_accessor :render_method + attr_reader :config - # Overrides irb's output method with Hirb::View.render_output. Takes an optional - # block which sets the view config. + # This activates view functionality i.e. the formatter, pager and size detection. If irb exists, it overrides irb's output + # method with Hirb::View.view_output. If using Wirble, you should call this after it. The view configuration + # can be specified in a hash via a config file, as options to this method, as this method's block or any combination of these three. + # In addition to the config keys mentioned in Hirb, the options also take the following keys: + # Options: + # * config_file: Name of config file to read. # Examples: - # Hirb.enable - # Hirb.enable {|c| c.output = {'String'=>{:class=>'Hirb::Helpers::Table'}} } - def enable(&block) + # Hirb::View.enable + # Hirb::View.enable :formatter=>false + # Hirb::View.enable {|c| c.output = {'String'=>{:class=>'Hirb::Helpers::Table'}} } + def enable(options={}, &block) return puts("Already enabled.") if @enabled @enabled = true - load_config(Hirb::HashStruct.block_to_hash(block)) - ::IRB::Irb.class_eval do - alias :non_hirb_render_output :output_value - def output_value #:nodoc: - Hirb::View.render_output(@context.last_value) || non_hirb_render_output + Hirb.config_file = options.delete(:config_file) if options[:config_file] + load_config(Util.recursive_hash_merge(options, HashStruct.block_to_hash(block))) + resize(config[:width], config[:height]) + if Object.const_defined?(:IRB) + ::IRB::Irb.class_eval do + alias :non_hirb_view_output :output_value + def output_value #:nodoc: + Hirb::View.view_output(@context.last_value) || Hirb::View.page_output(@context.last_value.inspect, true) || + non_hirb_view_output + end end end end - - # Disable's Hirb's output by reverting back to irb's. + + # Indicates if Hirb::View is enabled. + def enabled? + @enabled || false + end + + # Disable's Hirb's output and revert's irb's output method if irb exists. def disable @enabled = false - ::IRB::Irb.class_eval do - alias :output_value :non_hirb_render_output + if Object.const_defined?(:IRB) + ::IRB::Irb.class_eval do + alias :output_value :non_hirb_view_output + end end end + + # Toggles pager on or off. The pager only works while Hirb::View is enabled. + def toggle_pager + config[:pager] = !config[:pager] + end + + # Toggles formatter on or off. + def toggle_formatter + config[:formatter] = !config[:formatter] + end + + # Resizes the console width and height for use with the table and pager i.e. after having resized the console window. *nix users + # should only have to call this method. Non-*nix users should call this method with explicit width and height. If you don't know + # your width and height, in irb play with "a"* width to find width and puts "a\n" * height to find height. + def resize(width=nil, height=nil) + config[:width], config[:height] = determine_terminal_size(width, height) + pager.resize(config[:width], config[:height]) + end - # This is the main method of this class. This method searches for the first formatter it can apply - # to the object in this order: local block, method option, class option. If a formatter is found it applies it to the object - # and returns true. Returns false if no formatter found. - # ==== Options: - # [:method] Specifies a global (Kernel) method to do the formatting. - # [:class] Specifies a class to do the formatting, using its render() class method. The render() method's arguments are the output and - # an options hash. - # [:output_method] Specifies a method to call on output before passing it to a formatter. - # [:options] Options to pass the formatter method or class. - def render_output(output, options={}, &block) - if block && block.arity > 0 - formatted_output = block.call(output) - render_method.call(formatted_output) - true - elsif (formatted_output = format_output(output, options)) - render_method.call(formatted_output) - true - else - false - end + # This is the main method of this class. When view is enabled, this method searches for a formatter it can use for the output and if + # successful renders it using render_method(). The options this method takes are helper config hashes as described in + # Hirb::Formatter.format_output(). Returns true if successful and false if no formatting is done or if not enabled. + def view_output(output, options={}) + enabled? && config[:formatter] && render_output(output, options) end + # Captures STDOUT and renders it using render_method(). The main use case is to conditionally page captured stdout. + def capture_and_render(&block) + render_method.call Util.capture_stdout(&block) + end + # A lambda or proc which handles the final formatted object. - # Although this puts the object by default, it could be set to do other things - # ie write the formatted object to a file. + # Although this pages/puts the object by default, it could be set to do other things + # i.e. write the formatted object to a file. def render_method @render_method ||= default_render_method end + # Resets render_method back to its default. def reset_render_method @render_method = default_render_method end + + # Current console width + def width + config ? config[:width] : DEFAULT_WIDTH + end - # Config hash which maps classes to view hashes. View hashes are the same as the options hash of render_output(). - def output_config - config[:output] + # Current console height + def height + config ? config[:height] : DEFAULT_HEIGHT end - def output_config=(value) - @config[:output] = value + # Current formatter config + def formatter_config + formatter.config end - - # Needs to be called for config changes to take effect. Reloads Hirb::Views classes and registers - # most recent config changes. - def reload_config - current_config = self.config.dup.merge(:output=>output_config) - load_config(current_config) + + # Sets the helper config for the given output class. + def format_class(klass, helper_config) + formatter.format_class(klass, helper_config) end - - # A console version of render_output which takes its same options but allows for some shortcuts. - # Examples: - # console_render_output output, :tree, :type=>:directory - # # is the same as: - # render_output output, :class=>"Hirb::Helpers::Tree", :options=> {:type=>:directory} - # - def console_render_output(*args, &block) - load_config unless @config - output = args.shift - if args[0].is_a?(Symbol) && (view = args.shift) - symbol_options = find_view(view) - end - options = args[-1].is_a?(Hash) ? args[-1] : {} - options.merge!(symbol_options) if symbol_options - # iterates over format_output options that aren't :options - real_options = [:method, :class, :output_method].inject({}) do |h, e| - h[e] = options.delete(e) if options[e]; h - end - render_output(output, real_options.merge(:options=>options), &block) - end #:stopdoc: - def find_view(name) - name = name.to_s - if (view_method = output_config.values.find {|e| e[:method] == name }) - {:method=>view_method[:method]} - elsif (view_class = Hirb::Helpers.constants.find {|e| e == Util.camelize(name)}) - {:class=>"Hirb::Helpers::#{view_class}"} + def render_output(output, options={}) + if (formatted_output = formatter.format_output(output, options)) + render_method.call(formatted_output) + true else - {} + false end end - def format_output(output, options={}) - output_class = determine_output_class(output) - options = Util.recursive_hash_merge(output_class_options(output_class), options) - output = options[:output_method] ? (output.is_a?(Array) ? output.map {|e| e.send(options[:output_method])} : - output.send(options[:output_method]) ) : output - args = [output] - args << options[:options] if options[:options] && !options[:options].empty? - if options[:method] - new_output = send(options[:method],*args) - elsif options[:class] && (view_class = Util.any_const_get(options[:class])) - new_output = view_class.render(*args) - end - new_output + def determine_terminal_size(width, height) + detected = (width.nil? || height.nil?) ? Util.detect_terminal_size || [] : [] + [width || detected[0] || DEFAULT_WIDTH , height || detected[1] || DEFAULT_HEIGHT] end - def determine_output_class(output) - if output.is_a?(Array) - output[0].class + def page_output(output, inspect_mode=false) + if enabled? && config[:pager] && pager.activated_by?(output, inspect_mode) + pager.page(output, inspect_mode) + true else - output.class + false end end + def pager + @pager ||= Pager.new(config[:width], config[:height], :pager_command=>config[:pager_command]) + end + + def pager=(value); @pager = value; end + + def formatter(reload=false) + @formatter = reload || @formatter.nil? ? Formatter.new(config[:output]) : @formatter + end + + def formatter=(value); @formatter = value; end + def load_config(additional_config={}) - self.config = Util.recursive_hash_merge default_config, additional_config + @config = Util.recursive_hash_merge default_config, additional_config + formatter(true) true end - # Stores all view config. Current valid keys: - # :output- contains value of output_config - def config=(value) - reset_cached_output_config - @config = value + def config_loaded?; !!@config; end + + def config + @config end - def reset_cached_output_config - @cached_output_config = nil - end - - # Internal view options built from user-defined ones. Options are built by recursively merging options from oldest - # ancestors to the most recent ones. - def output_class_options(output_class) - @cached_output_config ||= {} - @cached_output_config[output_class] ||= - begin - output_ancestors_with_config = output_class.ancestors.map {|e| e.to_s}.select {|e| output_config.has_key?(e)} - @cached_output_config[output_class] = output_ancestors_with_config.reverse.inject({}) {|h, klass| - (klass == output_class.to_s || output_config[klass][:ancestor]) ? h.update(output_config[klass]) : h - } - end - @cached_output_config[output_class] - end - - def cached_output_config; @cached_output_config; end - def default_render_method - lambda {|output| puts output} + lambda {|output| page_output(output) || puts(output) } end def default_config - Hirb::Util.recursive_hash_merge({:output=>default_output_config}, Hirb.config[:view] || {} ) - end - - def default_output_config - Hirb::Views.constants.inject({}) {|h,e| - output_class = e.to_s.gsub("_", "::") - if (views_class = Hirb::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 - } + Util.recursive_hash_merge({:pager=>true, :formatter=>true}, Hirb.config || {}) end #:startdoc: end end