hirb 0.2.10 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,7 +1,17 @@
1
1
  module Hirb
2
2
  module Helpers #:nodoc:
3
+ @helper_classes ||= {}
4
+ def self.helper_class(klass)
5
+ @helper_classes[klass.to_s] ||= begin
6
+ if (helper_class = constants.find {|e| e.to_s == Util.camelize(klass.to_s)})
7
+ klass = "Hirb::Helpers::#{helper_class}"
8
+ end
9
+ Util.any_const_get(klass)
10
+ end
11
+ end
3
12
  end
4
13
  end
5
- %w{table object_table active_record_table auto_table tree parent_child_tree vertical_table}.each do |e|
14
+
15
+ %w{table object_table auto_table tree parent_child_tree vertical_table}.each do |e|
6
16
  require "hirb/helpers/#{e}"
7
17
  end
@@ -1,16 +1,24 @@
1
- # Detects the table class the output should use and delegates rendering to it.
2
- class Hirb::Helpers::AutoTable
3
- # Same options as Hirb::Helpers::Table.render.
1
+ # This helper wraps around the other table helpers i.e. Hirb::Helpers::Table while
2
+ # providing default helper options via Hirb::DynamicView. Using these default options, this
3
+ # helper supports views for the following modules/classes:
4
+ # ActiveRecord::Base, CouchFoo::Base, CouchPotato::Persistence, CouchRest::ExtendedDocument,
5
+ # DBI::Row, DataMapper::Resource, Friendly::Document, MongoMapper::Document, MongoMapper::EmbeddedDocument,
6
+ # Mongoid::Document, Ripple::Document, Sequel::Model.
7
+ class Hirb::Helpers::AutoTable < Hirb::Helpers::Table
8
+ extend Hirb::DynamicView
9
+
10
+ # Takes same options as Hirb::Helpers::Table.render except as noted below.
11
+ #
12
+ # ==== Options:
13
+ # [:table_class] Explicit table class to use for rendering. Defaults to
14
+ # Hirb::Helpers::ObjectTable if output is not an Array or Hash. Otherwise
15
+ # defaults to Hirb::Helpers::Table.
4
16
  def self.render(output, options={})
5
- options[:_original_class] = output.class
6
17
  output = Array(output)
7
- klass = if (output[0].is_a?(ActiveRecord::Base) rescue false)
8
- Hirb::Helpers::ActiveRecordTable
9
- elsif !(output[0].is_a?(Hash) || output[0].is_a?(Array))
10
- Hirb::Helpers::ObjectTable
11
- else
12
- Hirb::Helpers::Table
13
- end
18
+ (defaults = dynamic_options(output[0])) && (options = defaults.merge(options))
19
+ klass = options.delete(:table_class) || (
20
+ !(output[0].is_a?(Hash) || output[0].is_a?(Array)) ?
21
+ Hirb::Helpers::ObjectTable : Hirb::Helpers::Table)
14
22
  klass.render(output, options)
15
23
  end
16
- end
24
+ end
@@ -1,8 +1,8 @@
1
1
  class Hirb::Helpers::ObjectTable < Hirb::Helpers::Table
2
2
  # Rows are any ruby objects. Takes same options as Hirb::Helpers::Table.render except as noted below.
3
3
  #
4
- # Options:
5
- # :fields- Methods of the object to represent as columns. Defaults to [:to_s].
4
+ # ==== Options:
5
+ # [:fields] Methods of the object to represent as columns. Defaults to [:to_s].
6
6
  def self.render(rows, options ={})
7
7
  options[:fields] ||= [:to_s]
8
8
  options[:headers] ||= {:to_s=>'value'} if options[:fields] == [:to_s]
@@ -68,7 +68,7 @@ module Hirb
68
68
  # ==== Options:
69
69
  # [*:fields*] An array which overrides the default fields and can be used to indicate field order.
70
70
  # [*:headers*] A hash of fields and their header names. Fields that aren't specified here default to their name.
71
- # This option can also be an array but only for array rows.
71
+ # When set to false, headers are hidden. Can also be an array but only for array rows.
72
72
  # [*:max_fields*] A hash of fields and their maximum allowed lengths. Maximum length can also be a percentage of the total width
73
73
  # (decimal less than one). When a field exceeds it's maximum then it's
74
74
  # truncated and has a ... appended to it. Fields that aren't specified have no maximum.
@@ -90,7 +90,6 @@ module Hirb
90
90
  # [*:description*] When set to true, renders row count description at bottom. Default is true.
91
91
  # [*:escape_special_chars*] When set to true, escapes special characters \n,\t,\r so they don't disrupt tables. Default is false for
92
92
  # vertical tables and true for anything else.
93
- # [*:return_rows*] When set to true, returns rows that have been initialized but not rendered. Default is false.
94
93
  # Examples:
95
94
  # Hirb::Helpers::Table.render [[1,2], [2,3]]
96
95
  # Hirb::Helpers::Table.render [[1,2], [2,3]], :max_fields=>{0=>10}, :header_filter=>:capitalize
@@ -100,7 +99,7 @@ module Hirb
100
99
  # Hirb::Helpers::Table.render [{:age=>10, :weight=>100}, {:age=>80, :weight=>500}], :filters=>{:age=>[:to_f]}
101
100
  def render(rows, options={})
102
101
  options[:vertical] ? Helpers::VerticalTable.render(rows, options) :
103
- options[:return_rows] ? new(rows, options).instance_variable_get("@rows") : new(rows, options).render
102
+ new(rows, options).render
104
103
  rescue TooManyFieldsForWidthError
105
104
  $stderr.puts "", "** Error: Too many fields for the current width. Configure your width " +
106
105
  "and/or fields to avoid this error. Defaulting to a vertical table. **"
@@ -121,6 +120,8 @@ module Hirb
121
120
  #:stopdoc:
122
121
  attr_accessor :width, :max_fields, :field_lengths, :fields
123
122
  def initialize(rows, options={})
123
+ raise ArgumentError, "Table must be an array of hashes or array of arrays" unless rows.is_a?(Array) &&
124
+ (rows[0].is_a?(Hash) or rows[0].is_a?(Array) or rows.empty?)
124
125
  @options = {:description=>true, :filters=>{}, :change_fields=>{}, :escape_special_chars=>true,
125
126
  :filter_any=>Helpers::Table.filter_any, :resize=>true}.merge(options)
126
127
  @fields = set_fields(rows)
@@ -134,19 +135,18 @@ module Hirb
134
135
  end
135
136
 
136
137
  def set_fields(rows)
137
- fields = if @options[:fields]
138
- @options[:fields].dup
138
+ @options[:change_fields] = array_to_indices_hash(@options[:change_fields]) if @options[:change_fields].is_a?(Array)
139
+ return @options[:fields].dup if @options[:fields]
140
+
141
+ fields = if rows[0].is_a?(Hash)
142
+ keys = @options[:all_fields] ? rows.map {|e| e.keys}.flatten.uniq : rows[0].keys
143
+ keys.sort {|a,b| a.to_s <=> b.to_s}
139
144
  else
140
- if rows[0].is_a?(Hash)
141
- keys = @options[:all_fields] ? rows.map {|e| e.keys}.flatten.uniq : rows[0].keys
142
- keys.sort {|a,b| a.to_s <=> b.to_s}
143
- else
144
- rows[0].is_a?(Array) ? (0..rows[0].length - 1).to_a : []
145
- end
145
+ rows[0].is_a?(Array) ? (0..rows[0].length - 1).to_a : []
146
146
  end
147
- @options[:change_fields] = array_to_indices_hash(@options[:change_fields]) if @options[:change_fields].is_a?(Array)
147
+
148
148
  @options[:change_fields].each do |oldf, newf|
149
- (index = fields.index(oldf)) ? fields[index] = newf : fields << newf
149
+ (index = fields.index(oldf)) && fields[index] = newf
150
150
  end
151
151
  fields
152
152
  end
@@ -267,7 +267,8 @@ module Hirb
267
267
 
268
268
  # find max length for each field; start with the headers
269
269
  def default_field_lengths
270
- field_lengths = @headers ? @headers.inject({}) {|h,(k,v)| h[k] = String.size(v); h} : {}
270
+ field_lengths = @headers ? @headers.inject({}) {|h,(k,v)| h[k] = String.size(v); h} :
271
+ @fields.inject({}) {|h,e| h[e] = 1; h }
271
272
  @rows.each do |row|
272
273
  @fields.each do |field|
273
274
  len = String.size(row[field])
@@ -43,6 +43,7 @@ class Hirb::Helpers::Tree
43
43
  # [:indent] Number of spaces to indent between levels for basic + number trees. Default is 4.
44
44
  # [:limit] Limits the level or depth of a tree that is displayed. Root node is level 0.
45
45
  # [:description] Displays brief description about tree ie how many nodes it has.
46
+ # [:multi_line_nodes] Handles multi-lined nodes by indenting their newlines. Default is false.
46
47
  # Examples:
47
48
  # Hirb::Helpers::Tree.render([[0, 'root'], [1, 'child']], :type=>:directory)
48
49
  def render(nodes, options={})
@@ -81,28 +82,27 @@ class Hirb::Helpers::Tree
81
82
  @indent = ' ' * (@options[:indent] || 4 )
82
83
  @nodes = @nodes.select {|e| e[:level] <= @options[:limit] } if @options[:limit]
83
84
  case @type.to_s
84
- when 'directory'
85
- render_directory
86
- when 'number'
87
- render_number
88
- else
89
- render_basic
85
+ when 'directory' then render_directory
86
+ when 'number' then render_number
87
+ else render_basic
90
88
  end
91
89
  end
92
90
 
91
+ def render_nodes
92
+ value_indent = @options[:multi_line_nodes] ? @indent : nil
93
+ @nodes.map {|e| yield(e) + e.value(value_indent) }.join("\n")
94
+ end
95
+
93
96
  def render_directory
94
97
  mark_last_nodes_per_level
95
- new_nodes = []
96
- @nodes.each_with_index {|e, i|
98
+ render_nodes {|e|
97
99
  value = ''
98
100
  unless e.root?
99
101
  value << e.render_parent_characters
100
102
  value << (e[:last_node] ? "`-- " : "|-- ")
101
103
  end
102
- value << e[:value]
103
- new_nodes << value
104
+ value
104
105
  }
105
- new_nodes.join("\n")
106
106
  end
107
107
 
108
108
  def render_number
@@ -113,13 +113,13 @@ class Hirb::Helpers::Tree
113
113
  counter[parent_level_key] += 1
114
114
  e[:pre_value] = "#{counter[parent_level_key]}. "
115
115
  }
116
- @nodes.map {|e| @indent * e[:level] + e[:pre_value] + e[:value]}.join("\n")
116
+ render_nodes {|e| @indent * e[:level] + e[:pre_value] }
117
117
  end
118
-
118
+
119
119
  def render_basic
120
- @nodes.map {|e| @indent * e[:level] + e[:value]}.join("\n")
120
+ render_nodes {|e| @indent * e[:level] }
121
121
  end
122
-
122
+
123
123
  def validate_nodes
124
124
  @nodes.each do |e|
125
125
  raise ParentlessNodeError if (e[:level] > e.previous[:level]) && (e[:level] - e.previous[:level]) > 1
@@ -146,6 +146,10 @@ class Hirb::Helpers::Tree
146
146
  replace(hash)
147
147
  end
148
148
 
149
+ def value(indent=nil)
150
+ indent ? self[:value].gsub("\n", "\n#{indent * self[:level]}") : self[:value]
151
+ end
152
+
149
153
  def parent
150
154
  self[:tree].nodes.slice(0 .. self[:index]).reverse.detect {|e| e[:level] < self[:level]}
151
155
  end
@@ -2,7 +2,7 @@ module Hirb
2
2
  module ObjectMethods
3
3
  # Takes same options as Hirb::View.render_output.
4
4
  def view(*args)
5
- Hirb::View.console_render_output(*(args.unshift(self)))
5
+ Hirb::Console.render_output(*(args.unshift(self)))
6
6
  end
7
7
  end
8
8
  end
@@ -197,7 +197,7 @@ module Hirb
197
197
  end
198
198
 
199
199
  def table_helper_class?
200
- @options[:helper_class].is_a?(Class) && (@options[:helper_class] < Helpers::Table || @options[:helper_class] == Helpers::AutoTable)
200
+ @options[:helper_class].is_a?(Class) && @options[:helper_class] < Helpers::Table
201
201
  end
202
202
 
203
203
  def unalias_field(field)
@@ -16,7 +16,7 @@ module Hirb
16
16
  nil
17
17
  end
18
18
  end
19
-
19
+
20
20
  # Recursively merge hash1 with hash2.
21
21
  def recursive_hash_merge(hash1, hash2)
22
22
  hash1.merge(hash2) {|k,o,n| (o.is_a?(Hash)) ? recursive_hash_merge(o,n) : n}
@@ -0,0 +1,3 @@
1
+ module Hirb
2
+ VERSION = '0.3.0'
3
+ end
@@ -1,6 +1,64 @@
1
1
  module Hirb
2
- # This class is responsible for managing all view-related functionality. Its functionality is determined by setting up a configuration file
3
- # as explained in Hirb and/or passed configuration directly to Hirb.enable. Most of the functionality in this class is dormant until enabled.
2
+ # This class is responsible for managing all view-related functionality.
3
+ #
4
+ # == Create a View
5
+ # Let's create a simple view for Hash objects:
6
+ # $ irb -rubygems
7
+ # >> require 'hirb'
8
+ # =>true
9
+ # >> Hirb.enable
10
+ # =>nil
11
+ # >> require 'yaml'
12
+ # =>true
13
+ #
14
+ # # A view method is the smallest view
15
+ # >> def yaml(output); output.to_yaml; end
16
+ # => nil
17
+ # # Add the view
18
+ # >> Hirb.add_view Hash, :method=>:yaml
19
+ # => true
20
+ #
21
+ # # Hashes now appear as yaml
22
+ # >> {:a=>1, :b=>{:c=>3}}
23
+ # ---
24
+ # :a : 1
25
+ # :b :
26
+ # :c : 3
27
+ # => true
28
+ #
29
+ # Another way of creating a view is a Helper class:
30
+ #
31
+ # # Create yaml view class
32
+ # >> class Hirb::Helpers::Yaml; def self.render(output, options={}); output.to_yaml; end ;end
33
+ # =>nil
34
+ # # Add the view
35
+ # >> Hirb.add_view Hash, :class=>Hirb::Helpers::Yaml
36
+ # =>true
37
+ #
38
+ # # Hashes appear as yaml like above ...
39
+ #
40
+ # == Configure a View
41
+ # To configure the above Helper class as a view, either pass Hirb.enable a hash:
42
+ # # In .irbrc
43
+ # require 'hirb'
44
+ # # View class needs to come before enable()
45
+ # class Hirb::Helpers::Yaml; def self.render(output, options={}); output.to_yaml; end ;end
46
+ # Hirb.enable :output=>{"Hash"=>{:class=>"Hirb::Helpers::Yaml"}}
47
+ #
48
+ # Or create a config file at config/hirb.yml or ~/.hirb.yml:
49
+ # # The config file for the yaml example would look like:
50
+ # # ---
51
+ # # :output :
52
+ # # Hash :
53
+ # # :class : Hirb::Helpers::Yaml
54
+ #
55
+ # # In .irbrc
56
+ # require 'hirb'
57
+ # # View class needs to come before enable()
58
+ # class Hirb::Helpers::Yaml; def self.render(output, options={}); output.to_yaml; end ;end
59
+ # Hirb.enable
60
+ #
61
+ # For more about configuring Hirb, see the Config Files section in Hirb.
4
62
  module View
5
63
  DEFAULT_WIDTH = 120
6
64
  DEFAULT_HEIGHT = 40
@@ -10,26 +68,23 @@ module Hirb
10
68
 
11
69
  # This activates view functionality i.e. the formatter, pager and size detection. If irb exists, it overrides irb's output
12
70
  # method with Hirb::View.view_output. When called multiple times, new configs are merged into the existing config.
13
- # If using Wirble, you should call this after it. The view configuration
14
- # 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.
15
- # In addition to the config keys mentioned in Hirb, the options also take the following keys:
71
+ # If using Wirble, you should call this after it. The view configuration can be specified in a hash via a config file,
72
+ # or as options to this method. In addition to the config keys mentioned in Hirb, options also take the following keys:
16
73
  # ==== Options:
17
74
  # * config_file: Name of config file(s) that are merged into existing config
18
75
  # * output_method: Specify an object's class and instance method (separated by a period) to be realiased with
19
76
  # hirb's view system. The instance method should take a string to be output. Default is IRB::Irb.output_value
20
77
  # if using irb.
21
78
  # Examples:
22
- # Hirb::View.enable
23
- # Hirb::View.enable :formatter=>false, :output_method=>"Mini.output"
24
- # Hirb::View.enable {|c| c.output = {'String'=>{:class=>'Hirb::Helpers::Table'}} }
79
+ # Hirb.enable
80
+ # Hirb.enable :formatter=>false, :output_method=>"Mini.output"
25
81
  def enable(options={}, &block)
26
82
  Array(options.delete(:config_file)).each {|e|
27
83
  @new_config_file = true
28
84
  Hirb.config_files << e
29
85
  }
30
86
  enable_output_method(options.delete(:output_method))
31
- puts "Using a block with View.enable will be *deprecated* in the next release" if block_given?
32
- merge_or_load_config(Util.recursive_hash_merge(options, HashStruct.block_to_hash(block)))
87
+ merge_or_load_config options
33
88
  resize(config[:width], config[:height])
34
89
  @enabled = true
35
90
  end
@@ -102,14 +157,18 @@ module Hirb
102
157
  config ? config[:height] : DEFAULT_HEIGHT
103
158
  end
104
159
 
105
- # Current formatter config
160
+ # Current formatter config, storing a hash of all static views
106
161
  def formatter_config
107
162
  formatter.config
108
163
  end
109
164
 
110
- # Sets the helper config for the given output class.
111
- def format_class(klass, helper_config)
112
- formatter.format_class(klass, helper_config)
165
+ # Adds a view when View is enabled. See Formatter.add_view for more details.
166
+ def add(klass, view_config)
167
+ if enabled?
168
+ formatter.add_view(klass, view_config)
169
+ else
170
+ puts "View must be enabled to add a view"
171
+ end
113
172
  end
114
173
 
115
174
  #:stopdoc:
@@ -217,8 +276,4 @@ module Hirb
217
276
  #:startdoc:
218
277
  end
219
278
  end
220
-
221
- # Namespace for autoloaded views
222
- module Views
223
- end
224
- end
279
+ end
@@ -0,0 +1,8 @@
1
+ module Hirb
2
+ # Namespace for Helpers defining multiple views in a module i.e. via DynamicView.
3
+ module Views
4
+ module Single #:nodoc:
5
+ end
6
+ end
7
+ end
8
+ %w{rails orm mongo_db couch_db misc_db}.each {|e| require "hirb/views/#{e}" }
@@ -0,0 +1,11 @@
1
+ module Hirb::Views::CouchDb #:nodoc:
2
+ def default_couch(obj)
3
+ {:fields=>([:_id] + obj.class.properties.map {|e| e.name }) }
4
+ end
5
+
6
+ alias_method :couch_rest__extended_document_view, :default_couch
7
+ alias_method :couch_foo__base_view, :default_couch
8
+ alias_method :couch_potato__persistence_view, :default_couch
9
+ end
10
+
11
+ Hirb::DynamicView.add Hirb::Views::CouchDb, :helper=>:auto_table
@@ -0,0 +1,15 @@
1
+ module Hirb::Views::MiscDb #:nodoc:
2
+ def friendly__document_view(obj)
3
+ {:fields=>obj.class.attributes.keys - [:id]}
4
+ end
5
+
6
+ def ripple__document_view(obj)
7
+ {:fields=>obj.class.properties.keys}
8
+ end
9
+
10
+ def d_b_i__row_view(obj)
11
+ {:fields=>obj.column_names, :table_class=>Hirb::Helpers::Table}
12
+ end
13
+ end
14
+
15
+ Hirb::DynamicView.add Hirb::Views::MiscDb, :helper=>:auto_table
@@ -0,0 +1,15 @@
1
+ module Hirb::Views::MongoDb #:nodoc:
2
+ def mongoid__document_view(obj)
3
+ {:fields=>['_id'] + obj.class.fields.keys}
4
+ end
5
+
6
+ def mongo_mapper__document_view(obj)
7
+ {:fields=>obj.class.column_names}
8
+ end
9
+
10
+ def mongo_mapper__embedded_document_view(obj)
11
+ {:fields=>obj.class.column_names}
12
+ end
13
+ end
14
+
15
+ Hirb::DynamicView.add Hirb::Views::MongoDb, :helper=>:auto_table
@@ -0,0 +1,11 @@
1
+ module Hirb::Views::ORM #:nodoc:
2
+ def data_mapper__resource_view(obj)
3
+ {:fields=>obj.class.properties.map {|e| e.name }}
4
+ end
5
+
6
+ def sequel__model_view(obj)
7
+ {:fields=>obj.class.columns}
8
+ end
9
+ end
10
+
11
+ Hirb::DynamicView.add Hirb::Views::ORM, :helper=>:auto_table