cldwalker-hirb 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ The MIT LICENSE
2
+
3
+ Copyright (c) 2009 Gabriel Horner
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,216 @@
1
+ == Description
2
+
3
+ Hirb currently provides a mini view framework for console applications, designed with irb in mind.
4
+ This framework is activated by one method, which given the output of a console application, renders
5
+ a configured view based on the output's class. The framework encourages reusing views by letting you
6
+ package them in classes and associate them with any number of output classes. Hirb comes with table
7
+ views (see Hirb::Helpers::Table) which work out of the box with any output class, especially Rails'
8
+ model classes.
9
+
10
+ == Install
11
+
12
+ Install the gem with:
13
+
14
+ sudo gem install cldwalker-hirb -s http://gems.github.com
15
+
16
+ == Rails Example
17
+
18
+ Let's load and enable the view framework:
19
+ bash> script/console
20
+ Loading local environment (Rails 2.2.2)
21
+ irb>> require 'hirb'
22
+ => true
23
+ irb>> Hirb::View.enable
24
+ => nil
25
+
26
+ The default configuration provides table views for ActiveRecord::Base descendants.
27
+ If a class isn't configured, Hirb reverts to irb's default echo mode.
28
+ irb>> Hirb::View.output_config
29
+ => {"ActiveRecord::Base"=>{:class=>"Hirb::Views::ActiveRecord_Base", :ancestor=>true}}
30
+
31
+ # Tag is a model class and descendant of ActiveRecord::Base
32
+ irb>> Tag.last
33
+ +-----+-------------------------+-------------+---------------+-----------+-----------+-------+
34
+ | id | created_at | description | name | namespace | predicate | value |
35
+ +-----+-------------------------+-------------+---------------+-----------+-----------+-------+
36
+ | 907 | 2009-03-06 21:10:41 UTC | | gem:tags=yaml | gem | tags | yaml |
37
+ +-----+-------------------------+-------------+---------------+-----------+-----------+-------+
38
+ 1 row in set
39
+
40
+ irb>> 'plain ol irb'
41
+ => 'plain ol irb'
42
+ irb>> :blah
43
+ => :blah
44
+
45
+ From above you can see there were no views configured for a String or a Symbol so Hirb defaulted to
46
+ irb's echo mode. Also note that Tag was able to inherit its view from the ActiveRecord::Base config
47
+ because it had an :ancestor option.
48
+
49
+ Now that you understand that Hirb displays views based on an output object's class,
50
+ you may appreciate it also detects configured output objects in an array:
51
+
52
+ irb>> Tag.all :limit=>3, :order=>"id DESC"
53
+ +-----+-------------------------+-------------+-------------------+-----------+-----------+----------+
54
+ | id | created_at | description | name | namespace | predicate | value |
55
+ +-----+-------------------------+-------------+-------------------+-----------+-----------+----------+
56
+ | 907 | 2009-03-06 21:10:41 UTC | | gem:tags=yaml | gem | tags | yaml |
57
+ | 906 | 2009-03-06 08:47:04 UTC | | gem:tags=nomonkey | gem | tags | nomonkey |
58
+ | 905 | 2009-03-04 00:30:10 UTC | | article:tags=ruby | article | tags | ruby |
59
+ +-----+-------------------------+-------------+-------------------+-----------+-----------+----------+
60
+ 3 rows in set
61
+
62
+ At any time you can disable Hirb if you really like irb's lovely echo mode:
63
+ irb>> Hirb::View.disable
64
+ => nil
65
+ irb>> Tag.all :limit=>3, :order=>"id DESC"
66
+ => [#<Tag id: 907, name: "gem:tags=yaml", description: nil, created_at: "2009-03-06 21:10:41",
67
+ namespace: "gem", predicate: "tags", value: "yaml">, #<Tag id: 906, name: "gem:tags=nomonkey",
68
+ description: nil, created_at: "2009-03-06 08:47:04", namespace: "gem", predicate: "tags", value:
69
+ "nomonkey">, #<Tag id: 905, name: "article:tags=ruby", description: nil, created_at: "2009-03-04
70
+ 00:30:10", namespace: "article", predicate: "tags", value: "ruby">]
71
+
72
+ == Views: Anytime, Anywhere
73
+ While preconfigured tables are great for database records, sometimes you just want to create
74
+ tables/views for any output object:
75
+
76
+ #These examples don't need to have Hirb::View enabled.
77
+ irb>>Hirb::View.disable
78
+ =>nil
79
+
80
+ # Imports table() and view()
81
+ irb>>extend Hirb::Console
82
+ =>main
83
+
84
+ # Create a table of Dates comparing them with different formats.
85
+ irb>> table [Date.today, Date.today.next_month], :fields=>[:to_s, :ld, :ajd, :amjd, :asctime]
86
+ +------------+--------+-----------+-------+--------------------------+
87
+ | to_s | ld | ajd | amjd | asctime |
88
+ +------------+--------+-----------+-------+--------------------------+
89
+ | 2009-03-11 | 155742 | 4909803/2 | 54901 | Wed Mar 11 00:00:00 2009 |
90
+ | 2009-04-11 | 155773 | 4909865/2 | 54932 | Sat Apr 11 00:00:00 2009 |
91
+ +------------+--------+-----------+-------+--------------------------+
92
+ 2 rows in set
93
+
94
+ # Same table as the previous method. However view() will be able to call any view created.
95
+ irb>> view [Date.today, Date.today.next_month], :class=>Hirb::Helpers::ObjectTable,
96
+ :fields=>[:to_s, :ld, :ajd, :amjd, :asctime]
97
+
98
+ If these console methods weren't convenient enough, try:
99
+
100
+ # Imports view() to all objects.
101
+ irb>> require 'hirb/import_object'
102
+ =>true
103
+ # Yields same table as above examples.
104
+ irb>> [Date.today, Date.today.next_month].view :class=>Hirb::Helpers::ObjectTable,
105
+ :fields=>[:to_s, :ld, :ajd, :amjd, :asctime]
106
+
107
+ Although views by default are printed to STDOUT, they can be easily modified to write anywhere:
108
+ # Setup views to write to file 'console.log'.
109
+ irb>> Hirb::View.render_method = lambda {|output| File.open("console.log", 'w') {|f| f.write(output) } }
110
+
111
+ # Writes to file with same table output as above example.
112
+ irb>> view [Date.today, Date.today.next_month], :class=>Hirb::Helpers::ObjectTable,
113
+ :fields=>[:to_s, :ld, :ajd, :amjd, :asctime]
114
+
115
+ # Doesn't write to file because Symbol isn't configured to use Hirb::View and thus defaults to irb's echo mode.
116
+ irb>> :blah
117
+ =>:blah
118
+
119
+ # Go back to printing Hirb views to STDOUT.
120
+ irb>> Hirb::View.reset_render_method
121
+
122
+ == Create and Configure Views
123
+ Let's create a simple view and configure it in different ways to be Hash's default view:
124
+
125
+ === Setup
126
+ irb>> require 'hirb'
127
+ =>true
128
+ irb>> Hirb::View.enable
129
+ =>nil
130
+ irb>> require 'yaml'
131
+ =>true
132
+
133
+ === Configure As View Method
134
+ A view method is the smallest reuseable view.
135
+ # Create yaml view method
136
+ irb>> def yaml(output); output.to_yaml; end
137
+ =>nil
138
+
139
+ # Configure view and reload it
140
+ irb>>Hirb::View.output_config = {"Hash"=>{:method=>:yaml}}
141
+ =>{"Hash"=>{:method=>:yaml}}
142
+ irb>>Hash::View.reload_config
143
+ =>true
144
+
145
+ # Hashes now appear as yaml
146
+ irb>>{:a=>1, :b=>{:c=>3}}
147
+ ---
148
+ :a : 1
149
+ :b :
150
+ :c : 3
151
+ => true
152
+
153
+ === Configure As View Class
154
+ A view class is suited for more complex views. View classes can be under any namespace
155
+ and are expected to provide a render method. However, if a class is under the Hirb::Views namespace,
156
+ it will be automatically loaded with no configuration. Something to think about when
157
+ sharing views with others.
158
+
159
+ # Create yaml view class
160
+ irb>> class Hirb::Views::Hash; def self.render(output); output.to_yaml; end ;end
161
+ =>nil
162
+ # Just reload since no configuration is necessary
163
+ irb>>Hirb::View.reload_config
164
+
165
+ # Hashes now appear as yaml ...
166
+
167
+ Although the Hirb::Views namespace is great for quick classes that just plug and play, you
168
+ often want view classes that can be reused with multiple outputs. For this case, it's recommended to
169
+ use the Hirb::Helpers namespace.
170
+
171
+ # Create yaml view class
172
+ irb>> class Hirb::Helpers::Yaml; def self.render(output); output.to_yaml; end ;end
173
+ =>nil
174
+
175
+ # Configure view and reload it
176
+ irb>>Hirb::View.output_config = {"Hash"=>{:class=>"Hirb::Helpers::Yaml"}}
177
+ =>{"Hash"=>{:class=>"Hirb::Helpers::Yaml"}}
178
+ irb>>Hirb::View.reload_config
179
+
180
+ # Hashes now appear as yaml ...
181
+
182
+ === Configure At Startup
183
+ Once you know what views are associated with what output classes, you can configure
184
+ them at startup by passing Hirb::View.enable a block:
185
+ # In .irbrc
186
+ require 'hirb'
187
+ # View class needs to come before enable()
188
+ class Hirb::Helpers::Yaml; def self.render(output); output.to_yaml; end ;end
189
+ Hirb::View.enable {|conf| conf.output = {"Hash"=>{:class=>"Hirb::Helpers::Yaml"}} }
190
+
191
+ Or by creating a config file at config/hirb.yml or ~/.hirb.yml:
192
+ # The config file for the yaml example would look like:
193
+ # ---
194
+ # :view :
195
+ # :output :
196
+ # Hash :
197
+ # :class : Hirb::Helpers::Yaml
198
+
199
+ # In .irbrc
200
+ require 'hirb'
201
+ # View class needs to come before enable()
202
+ class Hirb::Helpers::Yaml; def self.render(output); output.to_yaml; end ;end
203
+ Hirb::View.enable
204
+
205
+ == Limitations
206
+ Although Hirb preserves Wirble colorizing irb's default echo mode, it doesn't colorize its own views.
207
+ This is mainly because colorizing caused table classes to render incorrectly. If you can get tables
208
+ and colors to work nicely, please fork. To colorize your Hirb output:
209
+ Hirb::View.render_method = lambda {|output| puts Wirble::Colorize.colorize(output) }
210
+
211
+ == Todo
212
+ * Create tree views.
213
+ * Possibly add non-view irb goodies ie command manager.
214
+ * Configurable max height, which if exceeded activates a pager.
215
+ * Provides helper methods to all view classes.
216
+ * Consider adding a template system as needed.
data/Rakefile ADDED
@@ -0,0 +1,49 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+ require 'rake/rdoctask'
4
+ begin
5
+ require 'rcov/rcovtask'
6
+
7
+ Rcov::RcovTask.new do |t|
8
+ t.libs << 'test'
9
+ t.test_files = FileList['test/**/*_test.rb']
10
+ t.rcov_opts = ["-T -x '/Library/Ruby/*'"]
11
+ t.verbose = true
12
+ end
13
+ rescue LoadError
14
+ puts "Rcov not available. Install it for rcov-related tasks with: sudo gem install rcov"
15
+ end
16
+
17
+ begin
18
+ require 'jeweler'
19
+ Jeweler::Tasks.new do |s|
20
+ s.name = "hirb"
21
+ s.description = "A mini view framework for console/irb that's easy to use, even while under its influence."
22
+ s.summary = s.description
23
+ s.email = "gabriel.horner@gmail.com"
24
+ s.homepage = "http://github.com/cldwalker/hirb"
25
+ s.authors = ["Gabriel Horner"]
26
+ s.has_rdoc = true
27
+ s.extra_rdoc_files = ["README.rdoc", "LICENSE.txt"]
28
+ s.files = FileList["Rakefile", "VERSION.yml", "README.rdoc", "LICENSE.txt", "{bin,lib,test}/**/*"]
29
+ end
30
+
31
+ rescue LoadError
32
+ puts "Jeweler not available. Install it for jeweler-related tasks with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
33
+ end
34
+
35
+ Rake::TestTask.new do |t|
36
+ t.libs << 'lib'
37
+ t.pattern = 'test/**/*_test.rb'
38
+ t.verbose = false
39
+ end
40
+
41
+ Rake::RDocTask.new do |rdoc|
42
+ rdoc.rdoc_dir = 'rdoc'
43
+ rdoc.title = 'test'
44
+ rdoc.options << '--line-numbers' << '--inline-source'
45
+ rdoc.rdoc_files.include('README*')
46
+ rdoc.rdoc_files.include('lib/**/*.rb')
47
+ end
48
+
49
+ task :default => :test
data/VERSION.yml ADDED
@@ -0,0 +1,4 @@
1
+ ---
2
+ :major: 0
3
+ :minor: 1
4
+ :patch: 0
@@ -0,0 +1,17 @@
1
+ module Hirb
2
+ # This class is meant to be extended to provide methods for use in a console/irb shell.
3
+ # For example:
4
+ # irb>> extend Hirb::Console
5
+ # irb>> view 'some string', :class=>Some::String::Formatter
6
+ # irb>> table [[:row1], [:row2]]
7
+ module Console
8
+ # Renders a table for the given object. Takes same options as Hirb::Helpers::Table.render.
9
+ def table(output, options={})
10
+ Hirb::View.console_render_output(output, options.merge(:class=>"Hirb::Helpers::AutoTable"))
11
+ end
12
+ # Renders any specified view for the given object. Takes same options as Hirb::View.render_output.
13
+ def view(*args)
14
+ Hirb::View.console_render_output(*args)
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ require 'ostruct'
2
+
3
+ class Hirb::HashStruct < OpenStruct #:nodoc:
4
+ def self.block_to_hash(block=nil)
5
+ config = self.new
6
+ if block
7
+ block.call(config)
8
+ config.to_hash
9
+ else
10
+ {}
11
+ end
12
+ end
13
+
14
+ def to_hash
15
+ @table
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ class Hirb::Helpers::ActiveRecordTable < Hirb::Helpers::ObjectTable
2
+ # Rows are Rails' ActiveRecord::Base objects.
3
+ # Takes same options as Hirb::Helpers::Table.render except as noted below.
4
+ #
5
+ # Options:
6
+ # :fields- Can be any attribute, column or not. If not given, this defaults to the database table's columns.
7
+ def self.render(rows, options={})
8
+ rows = [rows] unless rows.is_a?(Array)
9
+ options[:fields] ||=
10
+ begin
11
+ fields = rows.first.attribute_names
12
+ fields.unshift(fields.delete('id')) if fields.include?('id')
13
+ fields
14
+ end
15
+ super(rows, options)
16
+ end
17
+ end
@@ -0,0 +1,14 @@
1
+ # Attempts to autodetect the table class the output represents and delegates rendering to it.
2
+ class Hirb::Helpers::AutoTable
3
+ # Same options as Hirb::Helpers::Table.render.
4
+ def self.render(output, options={})
5
+ klass = if ((output.is_a?(Array) && output[0].is_a?(ActiveRecord::Base)) or output.is_a?(ActiveRecord::Base) rescue false)
6
+ Hirb::Helpers::ActiveRecordTable
7
+ elsif options[:fields]
8
+ Hirb::Helpers::ObjectTable
9
+ else
10
+ Hirb::Helpers::Table
11
+ end
12
+ klass.render(output, options)
13
+ end
14
+ end
@@ -0,0 +1,15 @@
1
+ class Hirb::Helpers::ObjectTable < Hirb::Helpers::Table
2
+ # Rows are any ruby objects. Takes same options as Hirb::Helpers::Table.render except as noted below.
3
+ #
4
+ # Options:
5
+ # :fields- Methods of the object which are represented as columns in the table. Required option.
6
+ # All method values are converted to strings via to_s.
7
+ def self.render(rows, options ={})
8
+ raise(ArgumentError, "Option 'fields' is required.") unless options[:fields]
9
+ rows = [rows] unless rows.is_a?(Array)
10
+ item_hashes = rows.inject([]) {|t,item|
11
+ t << options[:fields].inject({}) {|h,f| h[f] = item.send(f).to_s; h}
12
+ }
13
+ super(item_hashes, options)
14
+ end
15
+ end
@@ -0,0 +1,169 @@
1
+ # Base Table class from which other table classes inherit.
2
+ # By default, a table is constrained to a default width but this can be adjusted
3
+ # via options as well as Hirb:Helpers::Table.max_width.
4
+ # Rows can be an array of arrays or an array of hashes.
5
+ #
6
+ # An array of arrays ie [[1,2], [2,3]], would render:
7
+ # +---+---+
8
+ # | 0 | 1 |
9
+ # +---+---+
10
+ # | 1 | 2 |
11
+ # | 2 | 3 |
12
+ # +---+---+
13
+ #
14
+ # By default, the fields/columns are the numerical indices of the array.
15
+ #
16
+ # An array of hashes ie [{:age=>10, :weight=>100}, {:age=>80, :weight=>500}], would render:
17
+ # +-----+--------+
18
+ # | age | weight |
19
+ # +-----+--------+
20
+ # | 10 | 100 |
21
+ # | 80 | 500 |
22
+ # +-----+--------+
23
+ #
24
+ # By default, the fields/columns are the keys of the first hash.
25
+ #--
26
+ # derived from http://gist.github.com/72234
27
+ class Hirb::Helpers::Table
28
+ DEFAULT_MAX_WIDTH = 150
29
+ class << self
30
+ attr_accessor :max_width
31
+
32
+ # Main method which returns a formatted table.
33
+ # ==== Options:
34
+ # [:fields] An array which overrides the default fields and can be used to indicate field order.
35
+ # [:headers] A hash of fields and their header names. Fields that aren't specified here default to their name.
36
+ # This option can also be an array but only for array rows.
37
+ # [:field_lengths] A hash of fields and their maximum allowed lengths. If a field exceeds it's maximum
38
+ # length than it's truncated and has a ... appended to it. Fields that aren't specified here have no maximum allowed
39
+ # length.
40
+ # [:max_width] The maximum allowed width of all fields put together. This option is enforced except when the field_lengths option is set.
41
+ # Examples:
42
+ # Hirb::Helpers::Table.render [[1,2], [2,3]]
43
+ # Hirb::Helpers::Table.render [[1,2], [2,3]], :field_lengths=>{0=>10}
44
+ # Hirb::Helpers::Table.render [{:age=>10, :weight=>100}, {:age=>80, :weight=>500}]
45
+ # Hirb::Helpers::Table.render [{:age=>10, :weight=>100}, {:age=>80, :weight=>500}], :headers=>{:weight=>"Weight(lbs)"}
46
+ def render(rows, options={})
47
+ new(rows,options).render
48
+ end
49
+ end
50
+
51
+ #:stopdoc:
52
+ def initialize(rows, options={})
53
+ @options = options
54
+ @fields = options[:fields] || ((rows[0].is_a?(Hash)) ? rows[0].keys.sort {|a,b| a.to_s <=> b.to_s} :
55
+ rows[0].is_a?(Array) ? (0..rows[0].length - 1).to_a : [])
56
+ @rows = setup_rows(rows)
57
+ @headers = @fields.inject({}) {|h,e| h[e] = e.to_s; h}
58
+ if options.has_key?(:headers)
59
+ @headers = options[:headers].is_a?(Hash) ? @headers.merge(options[:headers]) :
60
+ (options[:headers].is_a?(Array) ? array_to_indices_hash(options[:headers]) : options[:headers])
61
+ end
62
+ end
63
+
64
+ def setup_rows(rows)
65
+ rows ||= []
66
+ rows = [rows] unless rows.is_a?(Array)
67
+ if rows[0].is_a?(Array)
68
+ rows = rows.inject([]) {|new_rows, row|
69
+ new_rows << array_to_indices_hash(row)
70
+ }
71
+ end
72
+ validate_values(rows)
73
+ rows
74
+ end
75
+
76
+ def render
77
+ body = []
78
+ unless @rows.length == 0
79
+ setup_field_lengths
80
+ body += @headers ? render_header : [render_border]
81
+ body += render_rows
82
+ body << render_border
83
+ end
84
+ body << render_table_description
85
+ body.join("\n")
86
+ end
87
+
88
+ def render_header
89
+ title_row = '| ' + @fields.map {|f|
90
+ format_cell(@headers[f], @field_lengths[f])
91
+ }.join(' | ') + ' |'
92
+ [render_border, title_row, render_border]
93
+ end
94
+
95
+ def render_border
96
+ '+-' + @fields.map {|f| '-' * @field_lengths[f] }.join('-+-') + '-+'
97
+ end
98
+
99
+ def format_cell(value, cell_width)
100
+ text = value.length > cell_width ?
101
+ (
102
+ (cell_width < 3) ? value.slice(0,cell_width) : value.slice(0, cell_width - 3) + '...'
103
+ ) : value
104
+ sprintf("%-#{cell_width}s", text)
105
+ end
106
+
107
+ def render_rows
108
+ @rows.map do |row|
109
+ row = '| ' + @fields.map {|f|
110
+ format_cell(row[f], @field_lengths[f])
111
+ }.join(' | ') + ' |'
112
+ end
113
+ end
114
+
115
+ def render_table_description
116
+ (@rows.length == 0) ? "0 rows in set" :
117
+ "#{@rows.length} #{@rows.length == 1 ? 'row' : 'rows'} in set"
118
+ end
119
+
120
+ def setup_field_lengths
121
+ @field_lengths = default_field_lengths
122
+ if @options[:field_lengths]
123
+ @field_lengths.merge!(@options[:field_lengths])
124
+ else
125
+ max_width = @options[:max_width] || Hirb::Helpers::Table.max_width || DEFAULT_MAX_WIDTH
126
+ restrict_field_lengths(@field_lengths, max_width)
127
+ end
128
+ end
129
+
130
+ # Simple algorithm which given a max width, allows smaller fields to be displayed while
131
+ # restricting longer fields at a new_long_field_length.
132
+ def restrict_field_lengths(field_lengths, max_width)
133
+ total_length = field_lengths.values.inject {|t,n| t += n}
134
+ if total_length > max_width
135
+ average_field_length = total_length / @fields.size.to_f
136
+ long_lengths, short_lengths = field_lengths.values.partition {|e| e > average_field_length}
137
+ new_long_field_length = (max_width - short_lengths.inject {|t,n| t += n}) / long_lengths.size
138
+ field_lengths.each {|f,length|
139
+ field_lengths[f] = new_long_field_length if length > new_long_field_length
140
+ }
141
+ end
142
+ end
143
+
144
+ # find max length for each field; start with the headers
145
+ def default_field_lengths
146
+ field_lengths = @headers ? @headers.inject({}) {|h,(k,v)| h[k] = v.length; h} : {}
147
+ @rows.each do |row|
148
+ @fields.each do |field|
149
+ len = row[field].length
150
+ field_lengths[field] = len if len > field_lengths[field].to_i
151
+ end
152
+ end
153
+ field_lengths
154
+ end
155
+
156
+ def validate_values(rows)
157
+ rows.each {|row|
158
+ @fields.each {|f|
159
+ row[f] = row[f].to_s || ''
160
+ }
161
+ }
162
+ end
163
+
164
+ # Converts an array to a hash mapping a numerical index to its array value.
165
+ def array_to_indices_hash(array)
166
+ array.inject({}) {|hash,e| hash[hash.size] = e; hash }
167
+ end
168
+ #:startdoc:
169
+ end
@@ -0,0 +1,7 @@
1
+ module Hirb
2
+ module Helpers #:nodoc:
3
+ end
4
+ end
5
+ %w{table object_table active_record_table auto_table}.each do |e|
6
+ require "hirb/helpers/#{e}"
7
+ end
@@ -0,0 +1,10 @@
1
+ module Hirb
2
+ module ObjectMethods
3
+ # Takes same options as Hirb::View.render_output.
4
+ def view(*args)
5
+ Hirb::View.console_render_output(*(args.unshift(self)))
6
+ end
7
+ end
8
+ end
9
+
10
+ Object.send :include, Hirb::ObjectMethods
data/lib/hirb/util.rb ADDED
@@ -0,0 +1,19 @@
1
+ module Hirb
2
+ module Util
3
+ extend self
4
+ # Returns a constant like const_get() no matter what namespace it's nested in.
5
+ # Returns nil if the constant is not found.
6
+ def any_const_get(name)
7
+ return name if name.is_a?(Module)
8
+ begin
9
+ klass = Object
10
+ name.split('::').each {|e|
11
+ klass = klass.const_get(e)
12
+ }
13
+ klass
14
+ rescue
15
+ nil
16
+ end
17
+ end
18
+ end
19
+ end
data/lib/hirb/view.rb ADDED
@@ -0,0 +1,179 @@
1
+ module Hirb
2
+ # This class contains one method, render_output, which formats and renders the output its given from a console application.
3
+ # However, this only happens for output classes that are configured to do so or if render_output is explicitly given
4
+ # a view formatter. The hash with the following keys are valid for Hirb::View.config as well as the :view key mentioned in Hirb:
5
+ # [:output] This hash is saved to output_config. It maps output classes to hashes that are passed to render_output. Thus these hashes
6
+ # take the same options as render_output. In addition it takes the following keys:
7
+ # * :ancestor- Boolean which if true allows all subclasses of the configured output class to inherit this config.
8
+ #
9
+ # Example: {'String'=>{:class=>'Hirb::Helpers::Table', :ancestor=>true, :options=>{:max_width=>180}}}
10
+ module View
11
+ class<<self
12
+ attr_accessor :config, :render_method
13
+
14
+ # Overrides irb's output method with Hirb::View.render_output. Takes an optional
15
+ # block which sets the view config.
16
+ # Examples:
17
+ # Hirb.enable
18
+ # Hirb.enable {|c| c.output = {'String'=>{:class=>'Hirb::Helpers::Table'}} }
19
+ def enable(&block)
20
+ return puts("Already enabled.") if @enabled
21
+ @enabled = true
22
+ load_config(Hirb::HashStruct.block_to_hash(block))
23
+ ::IRB::Irb.class_eval do
24
+ alias :non_hirb_render_output :output_value
25
+ def output_value #:nodoc:
26
+ Hirb::View.render_output(@context.last_value) || non_hirb_render_output
27
+ end
28
+ end
29
+ end
30
+
31
+ # Disable's Hirb's output by reverting back to irb's.
32
+ def disable
33
+ @enabled = false
34
+ ::IRB::Irb.class_eval do
35
+ alias :output_value :non_hirb_render_output
36
+ end
37
+ end
38
+
39
+ # This is the main method of this class. This method searches for the first formatter it can apply
40
+ # to the object in this order: local block, method option, class option. If a formatter is found it applies it to the object
41
+ # and returns true. Returns false if no formatter found.
42
+ # ==== Options:
43
+ # [:method] Specifies a global (Kernel) method to do the formatting.
44
+ # [:class] Specifies a class to do the formatting, using its render() class method.
45
+ # [:options] Options to pass the formatter method or class.
46
+ def render_output(output, options={}, &block)
47
+ if block && block.arity > 0
48
+ formatted_output = block.call(output)
49
+ render_method.call(formatted_output)
50
+ true
51
+ elsif (formatted_output = format_output(output, options))
52
+ render_method.call(formatted_output)
53
+ true
54
+ else
55
+ false
56
+ end
57
+ end
58
+
59
+ # A lambda or proc which handles the final formatted object.
60
+ # Although this puts the object by default, it could be set to do other things
61
+ # ie write the formatted object to a file.
62
+ def render_method
63
+ @render_method ||= default_render_method
64
+ end
65
+
66
+ def reset_render_method
67
+ @render_method = default_render_method
68
+ end
69
+
70
+ # Config hash which maps classes to view hashes. View hashes are the same as the options hash of render_output().
71
+ def output_config
72
+ @config[:output]
73
+ end
74
+
75
+ def output_config=(value)
76
+ @config[:output] = value
77
+ end
78
+
79
+ # Needs to be called for config changes to take effect. Reloads Hirb::Views classes and registers
80
+ # most recent config changes.
81
+ def reload_config
82
+ current_config = self.config.dup.merge(:output=>output_config)
83
+ load_config(current_config)
84
+ end
85
+
86
+ #:stopdoc:
87
+ def console_render_output(output, options={}, &block)
88
+ # iterates over format_output options that aren't :options
89
+ real_options = [:method, :class].inject({}) do |h, e|
90
+ h[e] = options.delete(e) if options[e]
91
+ h
92
+ end
93
+ render_output(output, real_options.merge(:options=>options), &block)
94
+ end
95
+
96
+ def format_output(output, options={})
97
+ output_class = determine_output_class(output)
98
+ options = output_class_options(output_class).merge(options)
99
+ args = [output]
100
+ args << options[:options] if options[:options] && !options[:options].empty?
101
+ if options[:method]
102
+ new_output = send(options[:method],*args)
103
+ elsif options[:class] && (view_class = Util.any_const_get(options[:class]))
104
+ new_output = view_class.render(*args)
105
+ end
106
+ new_output
107
+ end
108
+
109
+ def determine_output_class(output)
110
+ if output.is_a?(Array)
111
+ output[0].class
112
+ else
113
+ output.class
114
+ end
115
+ end
116
+
117
+ # Loads config
118
+ def load_config(additional_config={})
119
+ new_config = default_config
120
+ new_config[:output].merge!(additional_config.delete(:output) || {})
121
+ self.config = new_config.merge(additional_config)
122
+ true
123
+ end
124
+
125
+ # Stores all view config. Current valid keys:
126
+ # :output- contains value of output_config
127
+ def config=(value)
128
+ reset_cached_output_config
129
+ @config = value
130
+ end
131
+
132
+ def reset_cached_output_config
133
+ @cached_output_config = nil
134
+ end
135
+
136
+ # Internal view options built from user-defined ones. Options are built by recursively merging options from oldest
137
+ # ancestors to the most recent ones.
138
+ def output_class_options(output_class)
139
+ @cached_output_config ||= {}
140
+ @cached_output_config[output_class] ||=
141
+ begin
142
+ output_ancestors_with_config = output_class.ancestors.map {|e| e.to_s}.select {|e| output_config.has_key?(e)}
143
+ @cached_output_config[output_class] = output_ancestors_with_config.reverse.inject({}) {|h, klass|
144
+ (klass == output_class.to_s || output_config[klass][:ancestor]) ? h.update(output_config[klass]) : h
145
+ }
146
+ end
147
+ @cached_output_config[output_class]
148
+ end
149
+
150
+ def cached_output_config; @cached_output_config; end
151
+
152
+ def default_render_method
153
+ lambda {|output| puts output}
154
+ end
155
+
156
+ def default_config
157
+ conf = Hirb.config[:view] || {}
158
+ conf[:output] = default_output_config.merge(conf[:output] || {})
159
+ conf
160
+ end
161
+
162
+ def default_output_config
163
+ Hirb::Views.constants.inject({}) {|h,e|
164
+ output_class = e.to_s.gsub("_", "::")
165
+ if (views_class = Hirb::Views.const_get(e)) && views_class.respond_to?(:render)
166
+ default_options = views_class.respond_to?(:default_options) ? views_class.default_options : {}
167
+ h[output_class] = default_options.merge({:class=>"Hirb::Views::#{e}"})
168
+ end
169
+ h
170
+ }
171
+ end
172
+ #:startdoc:
173
+ end
174
+ end
175
+
176
+ # Namespace for autoloaded views
177
+ module Views
178
+ end
179
+ end
@@ -0,0 +1,9 @@
1
+ class Hirb::Views::ActiveRecord_Base #:nodoc:
2
+ def self.default_options
3
+ {:ancestor=>true}
4
+ end
5
+
6
+ def self.render(*args)
7
+ Hirb::Helpers::ActiveRecordTable.render(*args)
8
+ end
9
+ end
data/lib/hirb.rb ADDED
@@ -0,0 +1,31 @@
1
+ current_dir = File.dirname(__FILE__)
2
+ $:.unshift(current_dir) unless $:.include?(current_dir) || $:.include?(File.expand_path(current_dir))
3
+ require 'hirb/util'
4
+ require 'hirb/hash_struct'
5
+ require 'hirb/helpers'
6
+ require 'hirb/view'
7
+ require 'hirb/views/activerecord_base'
8
+ require 'hirb/console'
9
+
10
+ # Most of Hirb's functionality currently resides in Hirb::View.
11
+ # Hirb has an optional yaml config file defined by config_file. This config file
12
+ # has the following top level keys:
13
+ # [:view] See Hirb::View for the value of this entry.
14
+ module Hirb
15
+ class <<self
16
+ # Default is config/hirb.yml or ~/hirb.yml in that order.
17
+ def config_file
18
+ File.exists?('config/hirb.yml') ? 'config/hirb.yml' : File.expand_path(File.join("~",".hirb.yml"))
19
+ end
20
+
21
+ #:stopdoc:
22
+ def read_config_file(file=config_file)
23
+ File.exists?(file) ? YAML::load_file(file) : {}
24
+ end
25
+
26
+ def config(reload=false)
27
+ @config = (@config.nil? || reload) ? read_config_file : @config
28
+ end
29
+ #:startdoc:
30
+ end
31
+ end
data/test/hirb_test.rb ADDED
@@ -0,0 +1,23 @@
1
+ require File.join(File.dirname(__FILE__), 'test_helper')
2
+
3
+ class HirbTest < Test::Unit::TestCase
4
+ before(:each) {Hirb.instance_eval "@config = nil"}
5
+
6
+ test "config converts yaml when config file exists" do
7
+ yaml_data = {:blah=>'blah'}
8
+ File.stubs('exists?').returns(true)
9
+ YAML::expects(:load_file).returns(yaml_data)
10
+ Hirb.config.should == yaml_data
11
+ end
12
+
13
+ test "config defaults to hash when no config file" do
14
+ File.stubs('exists?').returns(false)
15
+ Hirb.config.should == {}
16
+ end
17
+
18
+ test "config reloads if given explicit reload" do
19
+ Hirb.config
20
+ Hirb.expects(:read_config_file)
21
+ Hirb.config(true)
22
+ end
23
+ end
@@ -0,0 +1,9 @@
1
+ require File.join(File.dirname(__FILE__), 'test_helper')
2
+
3
+ class Hirb::ImportTest < Test::Unit::TestCase
4
+ test "require import_object extends Object" do
5
+ Object.ancestors.map {|e| e.to_s}.include?("Hirb::ObjectMethods").should be(false)
6
+ require 'hirb/import_object'
7
+ Object.ancestors.map {|e| e.to_s}.include?("Hirb::ObjectMethods").should be(true)
8
+ end
9
+ end
@@ -0,0 +1,242 @@
1
+ require File.join(File.dirname(__FILE__), 'test_helper')
2
+
3
+ class Hirb::Helpers::TableTest < Test::Unit::TestCase
4
+ def table(*args)
5
+ Hirb::Helpers::Table.render(*args)
6
+ end
7
+
8
+ context "basic table" do
9
+ test "renders" do
10
+ expected_table = <<-TABLE.unindent
11
+ +---+---+
12
+ | a | b |
13
+ +---+---+
14
+ | 1 | 2 |
15
+ | 3 | 4 |
16
+ +---+---+
17
+ 2 rows in set
18
+ TABLE
19
+ table([{:a=>1, :b=>2}, {:a=>3, :b=>4}]).should == expected_table
20
+ end
21
+
22
+ test "with no headers renders" do
23
+ expected_table = <<-TABLE.unindent
24
+ +---+---+
25
+ | 1 | 2 |
26
+ +---+---+
27
+ 1 row in set
28
+ TABLE
29
+ table([{:a=>1, :b=>2}], :headers=>nil).should == expected_table
30
+ end
31
+
32
+ test "with string keys renders" do
33
+ expected_table = <<-TABLE.unindent
34
+ +---+---+
35
+ | a | b |
36
+ +---+---+
37
+ | 1 | 2 |
38
+ | 3 | 4 |
39
+ +---+---+
40
+ 2 rows in set
41
+ TABLE
42
+ table([{'a'=>1, 'b'=>2}, {'a'=>3, 'b'=>4}]).should == expected_table
43
+ end
44
+
45
+ test "with array only rows renders" do
46
+ expected_table = <<-TABLE.unindent
47
+ +---+---+
48
+ | 0 | 1 |
49
+ +---+---+
50
+ | 1 | 2 |
51
+ | 3 | 4 |
52
+ +---+---+
53
+ 2 rows in set
54
+ TABLE
55
+ table([[1,2], [3,4]]).should == expected_table
56
+ end
57
+
58
+ test "with no rows renders" do
59
+ table([]).should == "0 rows in set"
60
+ end
61
+ end
62
+
63
+ context "table with" do
64
+ test "fields option renders" do
65
+ expected_table = <<-TABLE.unindent
66
+ +---+---+
67
+ | b | a |
68
+ +---+---+
69
+ | 2 | 1 |
70
+ | 4 | 3 |
71
+ +---+---+
72
+ 2 rows in set
73
+ TABLE
74
+ table([{:a=>1, :b=>2}, {:a=>3, :b=>4}], :fields=>[:b, :a]).should == expected_table
75
+ end
76
+
77
+ test "fields option and array only rows" do
78
+ expected_table = <<-TABLE.unindent
79
+ +---+---+
80
+ | 0 | 2 |
81
+ +---+---+
82
+ | 1 | 3 |
83
+ +---+---+
84
+ 1 row in set
85
+ TABLE
86
+ table([[1,2,3]], :fields=>[0,2]).should == expected_table
87
+ end
88
+
89
+ test "invalid fields option renders empty columns" do
90
+ expected_table = <<-TABLE.unindent
91
+ +---+---+
92
+ | b | c |
93
+ +---+---+
94
+ | 2 | |
95
+ | 4 | |
96
+ +---+---+
97
+ 2 rows in set
98
+ TABLE
99
+ table([{:a=>1, :b=>2}, {:a=>3, :b=>4}], :fields=>[:b, :c]).should == expected_table
100
+ end
101
+
102
+ test "invalid fields in field_lengths option renders" do
103
+ expected_table = <<-TABLE.unindent
104
+ +------------+---+
105
+ | a | b |
106
+ +------------+---+
107
+ | AAAAAAA... | 2 |
108
+ +------------+---+
109
+ 1 row in set
110
+ TABLE
111
+ table([{:a=> "A" * 50, :b=>2}], :field_lengths=>{:a=>10,:c=>10}).should == expected_table
112
+ end
113
+
114
+ test "field_lengths option and field_lengths less than 3 characters renders" do
115
+ expected_table = <<-TABLE.unindent
116
+ +----+---+
117
+ | a | b |
118
+ +----+---+
119
+ | AA | 2 |
120
+ +----+---+
121
+ 1 row in set
122
+ TABLE
123
+ table([{:a=> "A" * 50, :b=>2}], :field_lengths=>{:a=>2}).should == expected_table
124
+ end
125
+
126
+ test "field_lengths option renders" do
127
+ expected_table = <<-TABLE.unindent
128
+ +------------+---+
129
+ | a | b |
130
+ +------------+---+
131
+ | AAAAAAA... | 2 |
132
+ +------------+---+
133
+ 1 row in set
134
+ TABLE
135
+ table([{:a=> "A" * 50, :b=>2}], :field_lengths=>{:a=>10}).should == expected_table
136
+ end
137
+
138
+ test "max_width option renders" do
139
+ expected_table = <<-TABLE.unindent
140
+ +---------------------+---+------------+
141
+ | a | b | c |
142
+ +---------------------+---+------------+
143
+ | AAAAAAAAAAAAAAAA... | 2 | CCCCCCCCCC |
144
+ +---------------------+---+------------+
145
+ 1 row in set
146
+ TABLE
147
+ table([{:a=> "A" * 50, :b=>2, :c=>"C"*10}], :max_width=>30).should == expected_table
148
+ end
149
+
150
+ test "global max_width renders" do
151
+ expected_table = <<-TABLE.unindent
152
+ +---------------------+---+------------+
153
+ | a | b | c |
154
+ +---------------------+---+------------+
155
+ | AAAAAAAAAAAAAAAA... | 2 | CCCCCCCCCC |
156
+ +---------------------+---+------------+
157
+ 1 row in set
158
+ TABLE
159
+ Hirb::Helpers::Table.max_width = 30
160
+ table([{:a=> "A" * 50, :b=>2, :c=>"C"*10}]).should == expected_table
161
+ Hirb::Helpers::Table.max_width = Hirb::Helpers::Table::DEFAULT_MAX_WIDTH
162
+ end
163
+
164
+ test "headers option and headers longer than fields renders" do
165
+ expected_table = <<-TABLE.unindent
166
+ +---+---------+---------+
167
+ | a | field B | field C |
168
+ +---+---------+---------+
169
+ | A | 2 | C |
170
+ +---+---------+---------+
171
+ 1 row in set
172
+ TABLE
173
+ table([{:a=> "A", :b=>2, :c=>"C"}], :headers=>{:b=>"field B", :c=>"field C"}).should == expected_table
174
+ end
175
+
176
+ test "headers option and headers shortened by field_lengths renders" do
177
+ expected_table = <<-TABLE.unindent
178
+ +-------+---+
179
+ | fi... | b |
180
+ +-------+---+
181
+ | A | 2 |
182
+ +-------+---+
183
+ 1 row in set
184
+ TABLE
185
+ table([{:a=> "A", :b=>2}], :headers=>{:a=>"field A"}, :field_lengths=>{:a=>5}).should == expected_table
186
+ end
187
+
188
+ test "with headers option as an array renders" do
189
+ expected_table = <<-TABLE.unindent
190
+ +---+---+
191
+ | A | B |
192
+ +---+---+
193
+ | 1 | 2 |
194
+ | 3 | 4 |
195
+ +---+---+
196
+ 2 rows in set
197
+ TABLE
198
+ table([[1,2], [3,4]], :headers=>['A', 'B']).should == expected_table
199
+ end
200
+
201
+ end
202
+
203
+ context "object table" do
204
+ before(:all) {
205
+ @pets = [stub(:name=>'rufus', :age=>7), stub(:name=>'alf', :age=>101)]
206
+ }
207
+ test "renders" do
208
+ expected_table = <<-TABLE.unindent
209
+ +-------+-----+
210
+ | name | age |
211
+ +-------+-----+
212
+ | rufus | 7 |
213
+ | alf | 101 |
214
+ +-------+-----+
215
+ 2 rows in set
216
+ TABLE
217
+ Hirb::Helpers::ObjectTable.render(@pets, :fields=>[:name, :age]).should == expected_table
218
+ end
219
+
220
+ test "with no options fields raises ArgumentError" do
221
+ assert_raises(ArgumentError) { Hirb::Helpers::ObjectTable.render(@pets) }
222
+ end
223
+ end
224
+
225
+ context "activerecord table" do
226
+ before(:all) {
227
+ @pets = [stub(:name=>'rufus', :age=>7, :attribute_names=>['age', 'name']), stub(:name=>'alf', :age=>101)]
228
+ }
229
+ test "renders" do
230
+ expected_table = <<-TABLE.unindent
231
+ +-----+-------+
232
+ | age | name |
233
+ +-----+-------+
234
+ | 7 | rufus |
235
+ | 101 | alf |
236
+ +-----+-------+
237
+ 2 rows in set
238
+ TABLE
239
+ Hirb::Helpers::ActiveRecordTable.render(@pets).should == expected_table
240
+ end
241
+ end
242
+ end
@@ -0,0 +1,16 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+ require 'context' #gem install jeremymcanally-context -s http://gems.github.com
4
+ require 'matchy' #gem install jeremymcanally-matchy -s http://gems.github.com
5
+ require 'mocha'
6
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
7
+ require 'hirb'
8
+
9
+ class Test::Unit::TestCase
10
+ end
11
+
12
+ class String
13
+ def unindent
14
+ gsub(/^\s*/, '').chomp
15
+ end
16
+ end
data/test/util_test.rb ADDED
@@ -0,0 +1,15 @@
1
+ require File.join(File.dirname(__FILE__), 'test_helper')
2
+
3
+ class Hirb::UtilTest < Test::Unit::TestCase
4
+ test "any_const_get returns nested class" do
5
+ Hirb::Util.any_const_get("Test::Unit").should == ::Test::Unit
6
+ end
7
+
8
+ test "any_const_get returns nil for invalid class" do
9
+ Hirb::Util.any_const_get("Basdfr").should == nil
10
+ end
11
+
12
+ test "any_const_get returns class when given class" do
13
+ Hirb::Util.any_const_get(String).should == String
14
+ end
15
+ end
data/test/view_test.rb ADDED
@@ -0,0 +1,151 @@
1
+ require File.join(File.dirname(__FILE__), 'test_helper')
2
+
3
+ # mocks IRB for testing
4
+ module ::IRB
5
+ class Irb
6
+ def initialize(context)
7
+ @context = context
8
+ end
9
+ def output_value; end
10
+ end
11
+ end
12
+
13
+ class Hirb::ViewTest < Test::Unit::TestCase
14
+ def set_config(value)
15
+ Hirb::View.output_config = value
16
+ Hirb::View.reset_cached_output_config
17
+ end
18
+
19
+ def output_config
20
+ Hirb::View.config[:output]
21
+ end
22
+
23
+ test "output_class_options merges ancestor options" do
24
+ set_config "String"=>{:args=>[1,2]}, "Object"=>{:method=>:object_output, :ancestor=>true}, "Kernel"=>{:method=>:default_output}
25
+ expected_result = {:method=>:object_output, :args=>[1, 2], :ancestor=>true}
26
+ Hirb::View.output_class_options(String).should == expected_result
27
+ end
28
+
29
+ test "output_class_options doesn't ancestor options" do
30
+ set_config "String"=>{:args=>[1,2]}, "Object"=>{:method=>:object_output}, "Kernel"=>{:method=>:default_output}
31
+ expected_result = {:args=>[1, 2]}
32
+ Hirb::View.output_class_options(String).should == expected_result
33
+ end
34
+
35
+ test "output_class_options returns hash when nothing found" do
36
+ Hirb::View.load_config
37
+ Hirb::View.output_class_options(String).should == {}
38
+ end
39
+
40
+ context "enable" do
41
+ before(:each) {Hirb::View.config = {}}
42
+ after(:each) { Hirb::View.disable }
43
+ test "redefines irb output_value" do
44
+ Hirb::View.expects(:render_output).once
45
+ Hirb::View.enable
46
+ context_stub = stub(:last_value=>'')
47
+ ::IRB::Irb.new(context_stub).output_value
48
+ end
49
+
50
+ test "sets default config" do
51
+ eval "module ::Hirb::Views::Something_Base; def self.render; end; end"
52
+ Hirb::View.enable
53
+ output_config["Something::Base"].should == {:class=>"Hirb::Views::Something_Base"}
54
+ end
55
+
56
+ test "sets default config with default_options" do
57
+ eval "module ::Hirb::Views::Blah; def self.render; end; def self.default_options; {:ancestor=>true}; end; end"
58
+ Hirb::View.enable
59
+ output_config["Blah"].should == {:class=>"Hirb::Views::Blah", :ancestor=>true}
60
+ end
61
+
62
+ test "with block sets config" do
63
+ class_hash = {"Something::Base"=>{:class=>"BlahBlah"}}
64
+ Hirb::View.enable {|c| c.output = class_hash }
65
+ output_config['Something::Base'].should == class_hash['Something::Base']
66
+ end
67
+ end
68
+
69
+ test "reload_config resets config to detect new Hirb::Views" do
70
+ Hirb::View.load_config
71
+ output_config.keys.include?('Zzz').should be(false)
72
+ eval "module ::Hirb::Views::Zzz; def self.render; end; end"
73
+ Hirb::View.reload_config
74
+ output_config.keys.include?('Zzz').should be(true)
75
+ end
76
+
77
+ test "reload_config picks up local changes" do
78
+ Hirb::View.load_config
79
+ output_config.keys.include?('Dooda').should be(false)
80
+ Hirb::View.output_config.merge!('Dooda'=>{:class=>"DoodaView"})
81
+ Hirb::View.reload_config
82
+ output_config['Dooda'].should == {:class=>"DoodaView"}
83
+ end
84
+
85
+ test "disable points output_value back to original output_value" do
86
+ Hirb::View.expects(:render_output).never
87
+ Hirb::View.enable
88
+ Hirb::View.disable
89
+ context_stub = stub(:last_value=>'')
90
+ ::IRB::Irb.new(context_stub).output_value
91
+ end
92
+
93
+ context "render_output" do
94
+ before(:all) {
95
+ eval %[module ::Commify
96
+ def self.render(strings)
97
+ strings = [strings] unless strings.is_a?(Array)
98
+ strings.map {|e| e.split('').join(',')}.join("\n")
99
+ end
100
+ end]
101
+ Hirb::View.enable
102
+ }
103
+ after(:all) { Hirb::View.disable }
104
+
105
+ test "formats with config method option" do
106
+ eval "module ::Kernel; def commify(string); string.split('').join(','); end; end"
107
+ set_config "String"=>{:method=>:commify}
108
+ Hirb::View.render_method.expects(:call).with('d,u,d,e')
109
+ Hirb::View.render_output('dude')
110
+ end
111
+
112
+ test "formats with config class option" do
113
+ set_config "String"=>{:class=>"Commify"}
114
+ Hirb::View.render_method.expects(:call).with('d,u,d,e')
115
+ Hirb::View.render_output('dude')
116
+ end
117
+
118
+ test "formats with output array" do
119
+ set_config "String"=>{:class=>"Commify"}
120
+ Hirb::View.render_method.expects(:call).with('d,u,d,e')
121
+ Hirb::View.render_output(['dude'])
122
+ end
123
+
124
+ test "formats with config options option" do
125
+ eval "module ::Blahify; def self.render(*args); end; end"
126
+ set_config "String"=>{:class=>"Blahify", :options=>{:fields=>%w{a b}}}
127
+ Blahify.expects(:render).with('dude', :fields=>%w{a b})
128
+ Hirb::View.render_output('dude')
129
+ end
130
+
131
+ test "doesn't format and returns false when no format method found" do
132
+ Hirb::View.load_config
133
+ Hirb::View.render_method.expects(:call).never
134
+ Hirb::View.render_output(Date.today).should == false
135
+ end
136
+
137
+ test "formats with explicit class option" do
138
+ set_config 'String'=>{:class=>"Blahify"}
139
+ Hirb::View.render_method.expects(:call).with('d,u,d,e')
140
+ Hirb::View.render_output('dude', :class=>"Commify")
141
+ end
142
+
143
+ test "formats with block" do
144
+ Hirb::View.load_config
145
+ Hirb::View.render_method.expects(:call).with('=dude=')
146
+ Hirb::View.render_output('dude') {|output|
147
+ "=#{output}="
148
+ }
149
+ end
150
+ end
151
+ end
metadata ADDED
@@ -0,0 +1,79 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cldwalker-hirb
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Gabriel Horner
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-03-12 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: A mini view framework for irb that's easy to use, even while under its influence.
17
+ email: gabriel.horner@gmail.com
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - README.rdoc
24
+ - LICENSE.txt
25
+ files:
26
+ - Rakefile
27
+ - VERSION.yml
28
+ - README.rdoc
29
+ - LICENSE.txt
30
+ - lib/hirb
31
+ - lib/hirb/console.rb
32
+ - lib/hirb/hash_struct.rb
33
+ - lib/hirb/helpers
34
+ - lib/hirb/helpers/active_record_table.rb
35
+ - lib/hirb/helpers/auto_table.rb
36
+ - lib/hirb/helpers/object_table.rb
37
+ - lib/hirb/helpers/table.rb
38
+ - lib/hirb/helpers.rb
39
+ - lib/hirb/import_object.rb
40
+ - lib/hirb/util.rb
41
+ - lib/hirb/view.rb
42
+ - lib/hirb/views
43
+ - lib/hirb/views/activerecord_base.rb
44
+ - lib/hirb.rb
45
+ - test/hirb_test.rb
46
+ - test/import_test.rb
47
+ - test/table_test.rb
48
+ - test/test_helper.rb
49
+ - test/util_test.rb
50
+ - test/view_test.rb
51
+ has_rdoc: true
52
+ homepage: http://github.com/cldwalker/hirb
53
+ post_install_message:
54
+ rdoc_options:
55
+ - --inline-source
56
+ - --charset=UTF-8
57
+ require_paths:
58
+ - lib
59
+ required_ruby_version: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ version: "0"
64
+ version:
65
+ required_rubygems_version: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: "0"
70
+ version:
71
+ requirements: []
72
+
73
+ rubyforge_project:
74
+ rubygems_version: 1.2.0
75
+ signing_key:
76
+ specification_version: 2
77
+ summary: A mini view framework for irb that's easy to use, even while under its influence.
78
+ test_files: []
79
+