hirb 0.2.10 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.rdoc +14 -0
- data/README.rdoc +67 -63
- data/Rakefile +3 -0
- data/lib/hirb.rb +25 -18
- data/lib/hirb/console.rb +3 -3
- data/lib/hirb/dynamic_view.rb +112 -0
- data/lib/hirb/formatter.rb +61 -140
- data/lib/hirb/helpers.rb +11 -1
- data/lib/hirb/helpers/auto_table.rb +20 -12
- data/lib/hirb/helpers/object_table.rb +2 -2
- data/lib/hirb/helpers/table.rb +15 -14
- data/lib/hirb/helpers/tree.rb +19 -15
- data/lib/hirb/import_object.rb +1 -1
- data/lib/hirb/menu.rb +1 -1
- data/lib/hirb/util.rb +1 -1
- data/lib/hirb/version.rb +3 -0
- data/lib/hirb/view.rb +74 -19
- data/lib/hirb/views.rb +8 -0
- data/lib/hirb/views/couch_db.rb +11 -0
- data/lib/hirb/views/misc_db.rb +15 -0
- data/lib/hirb/views/mongo_db.rb +15 -0
- data/lib/hirb/views/orm.rb +11 -0
- data/lib/hirb/views/rails.rb +19 -0
- data/test/dynamic_view_test.rb +96 -0
- data/test/formatter_test.rb +36 -48
- data/test/table_test.rb +41 -4
- data/test/tree_test.rb +17 -0
- data/test/view_test.rb +6 -0
- data/test/views_test.rb +15 -0
- metadata +24 -11
- data/VERSION.yml +0 -5
- data/lib/hirb/hash_struct.rb +0 -17
- data/lib/hirb/helpers/active_record_table.rb +0 -26
- data/lib/hirb/views/activerecord_base.rb +0 -9
- data/test/active_record_table_test.rb +0 -35
data/CHANGELOG.rdoc
CHANGED
@@ -1,3 +1,17 @@
|
|
1
|
+
== 0.3.0
|
2
|
+
* Added dynamic views.
|
3
|
+
* Added default table views for the following database classes/modules:
|
4
|
+
CouchFoo::Base, CouchPotato::Persistence, CouchRest::ExtendedDocument,
|
5
|
+
DBI::Row, DataMapper::Resource, Friendly::Document, MongoMapper::Document, MongoMapper::EmbeddedDocument,
|
6
|
+
Mongoid::Document, Ripple::Document and Sequel::Model.
|
7
|
+
* Added Hirb.add_view and Hirb.add_dynamic_view for easier view manipulation.
|
8
|
+
* Added :multi_line_nodes option for Tree.
|
9
|
+
* Fixed :change_fields option bug in Table.
|
10
|
+
* Fixed no headers and nil fields bug in Table.
|
11
|
+
* Removed deprecations in Hirb.config_file + View.enable.
|
12
|
+
* Removed Views classes and View.format_class.
|
13
|
+
* Removed :return_rows option for Table.
|
14
|
+
|
1
15
|
== 0.2.10
|
2
16
|
* Added multiple options to Menu, most importantly :two_d and :action.
|
3
17
|
* Improved table resizing algorithm.
|
data/README.rdoc
CHANGED
@@ -1,12 +1,14 @@
|
|
1
1
|
== Description
|
2
2
|
|
3
|
-
Hirb
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
3
|
+
Hirb provides a mini view framework for console applications and uses it to improve irb's default inspect output.
|
4
|
+
Given an object or array of objects, hirb renders a view based on the object's class and/or ancestry. Hirb offers reusable
|
5
|
+
views in the form of helper classes. The two main helpers, Hirb::Helpers::Table and Hirb::Helpers::Tree, provide several options
|
6
|
+
for generating ascii tables and trees. Using Hirb::Helpers::AutoTable, hirb has useful default views for at least ten popular database gems
|
7
|
+
i.e. Rails' ActiveRecord::Base. Other than views, hirb offers a smart pager and a console menu. The smart pager
|
8
|
+
only pages when the output exceeds the current screen size. The menu is used in conjunction with tables to offer
|
9
|
+
{two dimensional menus}[http://tagaholic.me/2010/02/16/two-dimensional-console-menus-with-hirb.html].
|
10
|
+
|
11
|
+
Note: To read a linked version of this README, {click here}[http://tagaholic.me/hirb/doc/].
|
10
12
|
|
11
13
|
== Install
|
12
14
|
|
@@ -14,40 +16,28 @@ Install the gem with:
|
|
14
16
|
|
15
17
|
sudo gem install hirb
|
16
18
|
|
17
|
-
==
|
18
|
-
|
19
|
-
Hirb has both pager and formatter functionality enabled by default.
|
20
|
-
If you want to turn off the functionality of either you can pass that in at startup:
|
19
|
+
== View Tutorials
|
21
20
|
|
22
|
-
|
23
|
-
|
21
|
+
* To create and configure views, see Hirb::View or {here if on the web}[http://tagaholic.me/hirb/doc/classes/Hirb/View.html].
|
22
|
+
* To create dynamic views, see Hirb::DynamicView or {here if on the web}[http://tagaholic.me/hirb/doc/classes/Hirb/DynamicView.html].
|
24
23
|
|
25
|
-
|
26
|
-
|
27
|
-
Hirb::View.toggle_pager
|
28
|
-
Hirb::View.toggle_formatter
|
29
|
-
|
30
|
-
== Create and Configure Views
|
31
|
-
|
32
|
-
If you'd like to learn how to create and configure views, {read the docs}[http://tagaholic.me/hirb/doc/classes/Hirb/Formatter.html].
|
33
|
-
|
34
|
-
== Rails Formatter Example
|
24
|
+
== Rails Example
|
35
25
|
|
36
26
|
Let's load and enable the view framework:
|
37
|
-
|
38
|
-
Loading local environment (Rails 2.
|
39
|
-
|
27
|
+
$ script/console
|
28
|
+
Loading local environment (Rails 2.3.5)
|
29
|
+
>> require 'hirb'
|
40
30
|
=> true
|
41
|
-
|
31
|
+
>> Hirb.enable
|
42
32
|
=> nil
|
43
33
|
|
44
34
|
The default configuration provides table views for ActiveRecord::Base descendants.
|
45
35
|
If a class isn't configured, Hirb reverts to irb's default echo mode.
|
46
|
-
|
47
|
-
=> {
|
36
|
+
>> Hirb::Formatter.dynamic_config['ActiveRecord::Base']
|
37
|
+
=> {:class=>Hirb::Helpers::AutoTable, :ancestor=>true}
|
48
38
|
|
49
39
|
# Tag is a model class and descendant of ActiveRecord::Base
|
50
|
-
|
40
|
+
>> Tag.last
|
51
41
|
+-----+-------------------------+-------------+---------------+-----------+-----------+-------+
|
52
42
|
| id | created_at | description | name | namespace | predicate | value |
|
53
43
|
+-----+-------------------------+-------------+---------------+-----------+-----------+-------+
|
@@ -55,19 +45,22 @@ If a class isn't configured, Hirb reverts to irb's default echo mode.
|
|
55
45
|
+-----+-------------------------+-------------+---------------+-----------+-----------+-------+
|
56
46
|
1 row in set
|
57
47
|
|
58
|
-
|
48
|
+
>> Hirb::Formatter.dynamic_config['String']
|
49
|
+
=> nil
|
50
|
+
>> 'plain ol irb'
|
59
51
|
=> 'plain ol irb'
|
60
|
-
|
52
|
+
>> Hirb::Formatter.dynamic_config['Symbol']
|
53
|
+
=> nil
|
54
|
+
>> :blah
|
61
55
|
=> :blah
|
62
56
|
|
63
|
-
From above you can see there
|
64
|
-
irb's echo mode.
|
65
|
-
|
57
|
+
From above you can see there are no views configured for a String or a Symbol so Hirb defaults to
|
58
|
+
irb's echo mode. On the other hand, Tag has a view thanks to being a descendant of ActiveRecord::Base
|
59
|
+
and there being an :ancestor option.
|
66
60
|
|
67
|
-
|
68
|
-
you may appreciate it also detects configured output objects in an array:
|
61
|
+
Having seen hirb display views based on an output object's class, let's see it handle an array of objects:
|
69
62
|
|
70
|
-
|
63
|
+
>> Tag.all :limit=>3, :order=>"id DESC"
|
71
64
|
+-----+-------------------------+-------------+-------------------+-----------+-----------+----------+
|
72
65
|
| id | created_at | description | name | namespace | predicate | value |
|
73
66
|
+-----+-------------------------+-------------+-------------------+-----------+-----------+----------+
|
@@ -78,9 +71,9 @@ you may appreciate it also detects configured output objects in an array:
|
|
78
71
|
3 rows in set
|
79
72
|
|
80
73
|
At any time you can disable Hirb if you really like irb's lovely echo mode:
|
81
|
-
|
74
|
+
>> Hirb.disable
|
82
75
|
=> nil
|
83
|
-
|
76
|
+
>> Tag.all :limit=>3, :order=>"id DESC"
|
84
77
|
=> [#<Tag id: 907, name: "gem:tags=yaml", description: nil, created_at: "2009-03-06 21:10:41",
|
85
78
|
namespace: "gem", predicate: "tags", value: "yaml">, #<Tag id: 906, name: "gem:tags=nomonkey",
|
86
79
|
description: nil, created_at: "2009-03-06 08:47:04", namespace: "gem", predicate: "tags", value:
|
@@ -92,15 +85,15 @@ While preconfigured tables are great for database records, sometimes you just wa
|
|
92
85
|
tables/views for any output object:
|
93
86
|
|
94
87
|
#These examples don't need to have Hirb::View enabled.
|
95
|
-
|
96
|
-
=>nil
|
88
|
+
>> Hirb.disable
|
89
|
+
=> nil
|
97
90
|
|
98
91
|
# Imports table() and view()
|
99
|
-
|
100
|
-
=>main
|
92
|
+
>> extend Hirb::Console
|
93
|
+
=> main
|
101
94
|
|
102
95
|
# Create a table of Dates comparing them with different formats.
|
103
|
-
|
96
|
+
>> table [Date.today, Date.today.next_month], :fields=>[:to_s, :ld, :ajd, :amjd, :asctime]
|
104
97
|
+------------+--------+-----------+-------+--------------------------+
|
105
98
|
| to_s | ld | ajd | amjd | asctime |
|
106
99
|
+------------+--------+-----------+-------+--------------------------+
|
@@ -110,44 +103,56 @@ tables/views for any output object:
|
|
110
103
|
2 rows in set
|
111
104
|
|
112
105
|
# Same table as the previous method. However view() will be able to call any helper.
|
113
|
-
|
106
|
+
>> view [Date.today, Date.today.next_month], :class=>:object_table,
|
114
107
|
:fields=>[:to_s, :ld, :ajd, :amjd, :asctime]
|
115
108
|
|
116
109
|
If these console methods weren't convenient enough, try:
|
117
110
|
|
118
111
|
# Imports view() to all objects.
|
119
|
-
|
112
|
+
>> require 'hirb/import_object'
|
120
113
|
=>true
|
121
114
|
# Yields same table as above examples.
|
122
|
-
|
115
|
+
>> [Date.today, Date.today.next_month].view :class=>:object_table,
|
123
116
|
:fields=>[:to_s, :ld, :ajd, :amjd, :asctime]
|
124
117
|
|
125
118
|
Although views by default are printed to STDOUT, they can be easily modified to write anywhere:
|
126
119
|
# Setup views to write to file 'console.log'.
|
127
|
-
|
120
|
+
>> Hirb::View.render_method = lambda {|output| File.open("console.log", 'w') {|f| f.write(output) } }
|
128
121
|
|
129
122
|
# Writes to file with same table output as above example.
|
130
|
-
|
123
|
+
>> view [Date.today, Date.today.next_month], :class=>:object_table,
|
131
124
|
:fields=>[:to_s, :ld, :ajd, :amjd, :asctime]
|
132
125
|
|
133
|
-
# Doesn't write to file because Symbol
|
134
|
-
|
126
|
+
# Doesn't write to file because Symbol doesn't have a view and thus defaults to irb's echo mode.
|
127
|
+
>> :blah
|
135
128
|
=>:blah
|
136
129
|
|
137
130
|
# Go back to printing Hirb views to STDOUT.
|
138
|
-
|
131
|
+
>> Hirb::View.reset_render_method
|
132
|
+
|
133
|
+
== Pager
|
134
|
+
|
135
|
+
Hirb has both pager and formatter functionality enabled by default.
|
136
|
+
If you want to turn off the functionality of either you can pass that in at startup:
|
137
|
+
|
138
|
+
Hirb.enable :pager=>false
|
139
|
+
Hirb.enable :formatter=>false
|
140
|
+
|
141
|
+
or toggle their state at runtime:
|
142
|
+
|
143
|
+
Hirb::View.toggle_pager
|
144
|
+
Hirb::View.toggle_formatter
|
139
145
|
|
140
|
-
== Sharing Views
|
141
|
-
If you have tested
|
142
|
-
|
143
|
-
|
146
|
+
== Sharing Helpers and Views
|
147
|
+
If you have tested helpers you'd like to share, fork Hirb and put them under lib/hirb/helpers. To share
|
148
|
+
views for certain classes, put them under lib/hirb/views. Please submit views for gems that have a nontrivial
|
149
|
+
number of users.
|
144
150
|
|
145
151
|
== Limitations
|
146
152
|
If using Wirble, you should call Hirb after it since they both override irb's default output.
|
147
153
|
|
148
154
|
== Motivation
|
149
|
-
Table code from http://gist.github.com/72234 and {my console
|
150
|
-
app's needs}[http://github.com/cldwalker/tag-tree].
|
155
|
+
Table code from http://gist.github.com/72234 and {my console app's needs}[http://github.com/cldwalker/tag-tree].
|
151
156
|
|
152
157
|
== Credits
|
153
158
|
* Chrononaut for vertical table helper.
|
@@ -162,7 +167,6 @@ Please report them {on github}[http://github.com/cldwalker/hirb/issues].
|
|
162
167
|
* http://tagaholic.me/2009/06/19/page-irb-output-and-improve-ri-with-hirb.html
|
163
168
|
|
164
169
|
== Todo
|
165
|
-
* Consider
|
166
|
-
*
|
167
|
-
*
|
168
|
-
* Consider adding a template system as needed.
|
170
|
+
* Consider generating views based on methods an object responds to.
|
171
|
+
* Provide helper methods to all views.
|
172
|
+
* Consider adding a template helper.
|
data/Rakefile
CHANGED
@@ -16,8 +16,11 @@ end
|
|
16
16
|
|
17
17
|
begin
|
18
18
|
require 'jeweler'
|
19
|
+
require File.dirname(__FILE__) + "/lib/hirb/version"
|
20
|
+
|
19
21
|
Jeweler::Tasks.new do |s|
|
20
22
|
s.name = "hirb"
|
23
|
+
s.version = Hirb::VERSION
|
21
24
|
s.summary = "A mini view framework for console/irb that's easy to use, even while under its influence."
|
22
25
|
s.description = "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."
|
23
26
|
s.email = "gabriel.horner@gmail.com"
|
data/lib/hirb.rb
CHANGED
@@ -6,28 +6,29 @@ $KCODE = 'u' if RUBY_VERSION < '1.9'
|
|
6
6
|
|
7
7
|
require 'hirb/util'
|
8
8
|
require 'hirb/string'
|
9
|
-
require 'hirb/
|
9
|
+
require 'hirb/formatter' # must come before helpers/auto_table
|
10
|
+
require 'hirb/dynamic_view'
|
10
11
|
require 'hirb/helpers'
|
12
|
+
require 'hirb/views'
|
11
13
|
require 'hirb/view'
|
12
|
-
require 'hirb/views/activerecord_base'
|
13
14
|
require 'hirb/console'
|
14
|
-
require 'hirb/formatter'
|
15
15
|
require 'hirb/pager'
|
16
16
|
require 'hirb/menu'
|
17
|
+
require 'hirb/version'
|
17
18
|
|
18
|
-
# Most of Hirb's functionality
|
19
|
-
# For
|
19
|
+
# Most of Hirb's functionality is in Hirb::View.
|
20
|
+
# For a tutorial on configuring and creating views see Hirb::View. For a tutorial on dynamic views see Hirb::DynamicView.
|
21
|
+
#
|
22
|
+
# == Config Files
|
20
23
|
# Hirb can have multiple config files defined by config_files(). These config files
|
21
24
|
# have the following top level keys:
|
22
|
-
# [
|
23
|
-
# [
|
24
|
-
# [
|
25
|
-
# [
|
26
|
-
# [
|
27
|
-
# [
|
28
|
-
#
|
29
|
-
#
|
30
|
-
|
25
|
+
# [*:output*] This hash is used by the formatter object. See Hirb::Formatter.config for its format.
|
26
|
+
# [*:width*] Width of the terminal/console. Defaults to Hirb::View::DEFAULT_WIDTH or possibly autodetected when Hirb is enabled.
|
27
|
+
# [*:height*] Height of the terminal/console. Defaults to Hirb::View::DEFAULT_HEIGHT or possibly autodetected when Hirb is enabled.
|
28
|
+
# [*:formatter*] Boolean which determines if the formatter is enabled. Defaults to true.
|
29
|
+
# [*:pager*] Boolean which determines if the pager is enabled. Defaults to true.
|
30
|
+
# [*:pager_command*] Command to be used for paging. Command can have options after it i.e. 'less -r'.
|
31
|
+
# Defaults to common pagers i.e. less and more if detected.
|
31
32
|
module Hirb
|
32
33
|
class <<self
|
33
34
|
attr_accessor :config_files, :config
|
@@ -42,6 +43,16 @@ module Hirb
|
|
42
43
|
View.disable
|
43
44
|
end
|
44
45
|
|
46
|
+
# Adds views. See Hirb::View.add for details.
|
47
|
+
def add_view(view, options, &block)
|
48
|
+
View.add(view, options, &block)
|
49
|
+
end
|
50
|
+
|
51
|
+
# Adds views. See Hirb::DynamicView.add for details.
|
52
|
+
def add_dynamic_view(view, options, &block)
|
53
|
+
DynamicView.add(view, options, &block)
|
54
|
+
end
|
55
|
+
|
45
56
|
# Array of config files which are merged sequentially to produce config.
|
46
57
|
# Defaults to config/hirb.yml and ~/.hirb_yml
|
47
58
|
def config_files
|
@@ -49,10 +60,6 @@ module Hirb
|
|
49
60
|
end
|
50
61
|
|
51
62
|
#:stopdoc:
|
52
|
-
def config_file
|
53
|
-
puts "Hirb.config_file is *deprecated*. Use Hirb.config_files"
|
54
|
-
end
|
55
|
-
|
56
63
|
def default_config_files
|
57
64
|
[File.join(Util.find_home, ".hirb.yml")] +
|
58
65
|
(File.exists?('config/hirb.yml') ? ['config/hirb.yml'] : [])
|
data/lib/hirb/console.rb
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
module Hirb
|
2
2
|
# This module is meant to be extended to provide methods for use in a console/irb shell.
|
3
3
|
# For example:
|
4
|
-
#
|
5
|
-
#
|
6
|
-
#
|
4
|
+
# >> extend Hirb::Console
|
5
|
+
# >> view 'some string', :class=>Some::String::Formatter
|
6
|
+
# >> table [[:row1], [:row2]]
|
7
7
|
module Console
|
8
8
|
class<<self
|
9
9
|
# A console version of render_output() which takes its same options but allows for shorthand. All options are passed to
|
@@ -0,0 +1,112 @@
|
|
1
|
+
module Hirb
|
2
|
+
# This module extends a Helper with the ability to have dynamic views for configured output classes.
|
3
|
+
# After a Helper has extended this module, it can use it within a render() by calling
|
4
|
+
# dynamic_options() to get dynamically generated options for the object it's rendering. See Hirb::Helpers::AutoTable as an example.
|
5
|
+
#
|
6
|
+
# == Dynamic Views
|
7
|
+
# Whereas normal views are generated from helpers with static helper options, dynamic views are generated from helpers and
|
8
|
+
# dynamically generated helper options. Let's look at an example for Rails' ActiveRecord classes:
|
9
|
+
#
|
10
|
+
# Hirb.add_dynamic_view("ActiveRecord::Base", :helper=>:auto_table) {|obj|
|
11
|
+
# {:fields=>obj.class.column_names} }
|
12
|
+
#
|
13
|
+
# From this dynamic view definition, _any_ ActiveRecord model class will render a table with the correct fields, since the fields
|
14
|
+
# are extracted from the output object's class at runtime. Note that dynamic view definitions should return a hash of helper options.
|
15
|
+
#
|
16
|
+
# To define multiple dynamic views, create a Views module where each method ending in '\_view' maps to a class/module:
|
17
|
+
#
|
18
|
+
# module Hirb::Views::ORM
|
19
|
+
# def data_mapper__resource_view(obj)
|
20
|
+
# {:fields=>obj.class.properties.map {|e| e.name }}
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# def sequel__model_view(obj)
|
24
|
+
# {:fields=>obj.class.columns}
|
25
|
+
# end
|
26
|
+
# end
|
27
|
+
#
|
28
|
+
# Hirb.add_dynamic_view Hirb::Views::ORM, :helper=>:auto_table
|
29
|
+
#
|
30
|
+
# In this example, 'data_mapper__resource_view' maps to DataMapper::Resource and 'sequel__model_view' maps to Sequel::Model.
|
31
|
+
# Note that when mapping method names to class names, '__' maps to '::' and '_' signals the next letter to be capitalized.
|
32
|
+
module DynamicView
|
33
|
+
# Add dynamic views to output class(es) for a given helper. If defining one view, the first argument is the output class
|
34
|
+
# and a block defines the dynamic view. If defining multiple views, the first argument should be a Views::* module where
|
35
|
+
# each method in the module ending in _view defines a view for an output class. To map output classes to method names in
|
36
|
+
# a Views module, translate'::' to '__' and a capital letter translates to a '_' and a lowercase letter.
|
37
|
+
# ==== Options:
|
38
|
+
# [*:helper*] Required option. Helper class that view(s) use to format. Hirb::Helpers::AutoTable is the only valid
|
39
|
+
# helper among default helpers. Can be given in aliased form i.e. :auto_table -> Hirb::Helpers::AutoTable.
|
40
|
+
#
|
41
|
+
# Examples:
|
42
|
+
# Hirb.add_dynamic_view Hirb::Views::ORM, :helper=>:auto_table
|
43
|
+
# Hirb.add_dynamic_view("ActiveRecord::Base", :helper=>:auto_table) {|obj| {:fields=>obj.class.column_names} }
|
44
|
+
def self.add(view, options, &block)
|
45
|
+
raise ArgumentError, ":helper option is required" unless options[:helper]
|
46
|
+
helper = Helpers.helper_class options[:helper]
|
47
|
+
unless helper.is_a?(Module) && class << helper; self.ancestors; end.include?(self)
|
48
|
+
raise ArgumentError, ":helper option must be a helper that has extended DynamicView"
|
49
|
+
end
|
50
|
+
mod = block ? generate_single_view_module(view, &block) : view
|
51
|
+
raise ArgumentError, "'#{mod}' must be a module" unless mod.is_a?(Module)
|
52
|
+
helper.add_module mod
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.generate_single_view_module(output_mod, &block) #:nodoc:
|
56
|
+
mod_name = class_to_method output_mod.to_s
|
57
|
+
Views::Single.send(:remove_const, mod_name) if Views::Single.const_defined?(mod_name)
|
58
|
+
mod = Views::Single.const_set(mod_name, Module.new)
|
59
|
+
mod.send(:define_method, mod_name.downcase, block)
|
60
|
+
mod
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.class_to_method(mod) #:nodoc:
|
64
|
+
mod.gsub("::","__").gsub(/([A-Z])/, '_\1').gsub(/(^|::)_/,'\1') + '_view'
|
65
|
+
end
|
66
|
+
|
67
|
+
# Returns a hash of options based on dynamic views defined for the object's ancestry. If no config is found returns nil.
|
68
|
+
def dynamic_options(obj)
|
69
|
+
view_methods.each do |meth|
|
70
|
+
if obj.class.ancestors.map {|e| e.to_s }.include?(method_to_class(meth))
|
71
|
+
begin
|
72
|
+
return send(meth, obj)
|
73
|
+
rescue
|
74
|
+
raise "View failed to generate for '#{method_to_class(meth)}' "+
|
75
|
+
"while in '#{meth}' with error:\n#{$!.message}"
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
nil
|
80
|
+
end
|
81
|
+
|
82
|
+
#:stopdoc:
|
83
|
+
def add_module(mod)
|
84
|
+
new_methods = mod.instance_methods.select {|e| e.to_s =~ /_view$/ }.map {|e| e.to_s}
|
85
|
+
return if new_methods.empty?
|
86
|
+
extend mod
|
87
|
+
view_methods.replace(view_methods + new_methods).uniq!
|
88
|
+
update_config(new_methods)
|
89
|
+
end
|
90
|
+
|
91
|
+
def update_config(meths)
|
92
|
+
output_config = meths.inject({}) {|t,e|
|
93
|
+
t[method_to_class(e)] = {:class=>self, :ancestor=>true}; t
|
94
|
+
}
|
95
|
+
Formatter.dynamic_config.merge! output_config
|
96
|
+
end
|
97
|
+
|
98
|
+
def method_to_class(meth)
|
99
|
+
view_method_classes[meth] ||= Util.camelize meth.sub(/_view$/, '').gsub('__', '/')
|
100
|
+
end
|
101
|
+
|
102
|
+
def view_method_classes
|
103
|
+
@view_method_classes ||= {}
|
104
|
+
end
|
105
|
+
#:startdoc:
|
106
|
+
|
107
|
+
# Stores view methods that a Helper has been given via DynamicView.add
|
108
|
+
def view_methods
|
109
|
+
@view_methods ||= []
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
data/lib/hirb/formatter.rb
CHANGED
@@ -1,160 +1,77 @@
|
|
1
1
|
module Hirb
|
2
|
-
|
3
|
-
|
4
|
-
The formatter object looks for an output's class config in Hirb::Formatter.config and if found applies a helper to the output.
|
5
|
-
|
6
|
-
== Create and Configure Views
|
7
|
-
Let's create a simple view and configure it in different ways to be Hash's default view:
|
8
|
-
|
9
|
-
=== Setup
|
10
|
-
irb>> require 'hirb'
|
11
|
-
=>true
|
12
|
-
irb>> Hirb.enable
|
13
|
-
=>nil
|
14
|
-
irb>> require 'yaml'
|
15
|
-
=>true
|
16
|
-
|
17
|
-
=== Configure As View Method
|
18
|
-
A view method is the smallest reuseable view.
|
19
|
-
# Create yaml view method
|
20
|
-
irb>> def yaml(output); output.to_yaml; end
|
21
|
-
=>nil
|
22
|
-
|
23
|
-
# Configure view
|
24
|
-
irb>>Hirb::View.format_class Hash, :method=>:yaml
|
25
|
-
=>true
|
26
|
-
|
27
|
-
# Hashes now appear as yaml
|
28
|
-
irb>>{:a=>1, :b=>{:c=>3}}
|
29
|
-
---
|
30
|
-
:a : 1
|
31
|
-
:b :
|
32
|
-
:c : 3
|
33
|
-
=> true
|
34
|
-
|
35
|
-
=== Configure As View Class
|
36
|
-
A view class is suited for more complex views. View classes can be under any namespace
|
37
|
-
and are expected to provide a render method. However, if a class is under the Hirb::Views namespace,
|
38
|
-
it will be automatically loaded with no configuration. Something to think about when
|
39
|
-
sharing views with others.
|
40
|
-
|
41
|
-
# Create yaml view class
|
42
|
-
irb>> class Hirb::Views::Hash; def self.render(output, options={}); output.to_yaml; end ;end
|
43
|
-
=>nil
|
44
|
-
# Just reload since no configuration is necessary
|
45
|
-
irb>>Hirb::View.formatter.reload
|
46
|
-
|
47
|
-
# Hashes now appear as yaml ...
|
48
|
-
|
49
|
-
Although the Hirb::Views namespace is great for quick classes that just plug and play, you
|
50
|
-
often want view classes that can be reused with multiple outputs. For this case, it's recommended to
|
51
|
-
use the Hirb::Helpers namespace.
|
52
|
-
|
53
|
-
# Create yaml view class
|
54
|
-
irb>> class Hirb::Helpers::Yaml; def self.render(output, options={}); output.to_yaml; end ;end
|
55
|
-
=>nil
|
56
|
-
|
57
|
-
# Configure view and reload it
|
58
|
-
irb>>Hirb::View.format_class Hash, :class=>"Hirb::Helpers::Yaml"
|
59
|
-
=>true
|
60
|
-
|
61
|
-
# Hashes now appear as yaml ...
|
62
|
-
|
63
|
-
== Configure At Startup
|
64
|
-
Once you know what views are associated with what output classes, you can configure
|
65
|
-
them at startup by passing Hirb.enable an options hash:
|
66
|
-
# In .irbrc
|
67
|
-
require 'hirb'
|
68
|
-
# View class needs to come before enable()
|
69
|
-
class Hirb::Helpers::Yaml; def self.render(output, options={}); output.to_yaml; end ;end
|
70
|
-
Hirb.enable :output=>{"Hash"=>{:class=>"Hirb::Helpers::Yaml"}}
|
71
|
-
|
72
|
-
Or by creating a config file at config/hirb.yml or ~/.hirb.yml:
|
73
|
-
# The config file for the yaml example would look like:
|
74
|
-
# ---
|
75
|
-
# :output :
|
76
|
-
# Hash :
|
77
|
-
# :class : Hirb::Helpers::Yaml
|
78
|
-
|
79
|
-
# In .irbrc
|
80
|
-
require 'hirb'
|
81
|
-
# View class needs to come before enable()
|
82
|
-
class Hirb::Helpers::Yaml; def self.render(output, options={}); output.to_yaml; end ;end
|
83
|
-
Hirb.enable
|
84
|
-
=end
|
85
|
-
|
2
|
+
# A Formatter object formats an output object (using Formatter.format_output) into a string based on the views defined
|
3
|
+
# for its class and/or ancestry.
|
86
4
|
class Formatter
|
87
|
-
|
5
|
+
class<<self
|
6
|
+
# This config is used by Formatter.format_output to lazily load dynamic views defined with Hirb::DynamicView.
|
7
|
+
# This hash has the same format as Formatter.config.
|
8
|
+
attr_accessor :dynamic_config
|
9
|
+
end
|
10
|
+
self.dynamic_config = {}
|
11
|
+
|
12
|
+
def initialize(additional_config={}) #:nodoc:
|
88
13
|
@klass_config = {}
|
89
|
-
@config =
|
14
|
+
@config = additional_config || {}
|
90
15
|
end
|
91
16
|
|
92
|
-
# A hash of Ruby class strings mapped to
|
93
|
-
# or :class option for a
|
94
|
-
# [
|
95
|
-
# [
|
96
|
-
#
|
97
|
-
# [
|
98
|
-
#
|
99
|
-
# [
|
100
|
-
# [
|
101
|
-
#
|
17
|
+
# A hash of Ruby class strings mapped to view hashes. A view hash must have at least a :method, :output_method
|
18
|
+
# or :class option for a view to be applied to an output. A view hash has the following keys:
|
19
|
+
# [*:method*] Specifies a global (Kernel) method to do the formatting.
|
20
|
+
# [*:class*] Specifies a class to do the formatting, using its render() class method. If a symbol it's converted to a corresponding
|
21
|
+
# Hirb::Helpers::* class if it exists.
|
22
|
+
# [*: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
|
23
|
+
# to every element in the array.
|
24
|
+
# [*:options*] Options to pass the helper method or class.
|
25
|
+
# [*:ancestor*] Boolean which when true causes subclasses of the output class to inherit its config. This doesn't effect the current
|
26
|
+
# output class. Defaults to false. This is used by ActiveRecord classes.
|
102
27
|
#
|
103
28
|
# Examples:
|
104
29
|
# {'WWW::Delicious::Element'=>{:class=>'Hirb::Helpers::ObjectTable', :ancestor=>true, :options=>{:max_width=>180}}}
|
105
30
|
# {'Date'=>{:class=>:auto_table, :ancestor=>true}}
|
31
|
+
# {'Hash'=>{:method=>:puts}}
|
106
32
|
def config
|
107
33
|
@config
|
108
34
|
end
|
109
35
|
|
110
|
-
#
|
111
|
-
def
|
36
|
+
# Adds the view for the given class and view hash config. See Formatter.config for valid keys for view hash.
|
37
|
+
def add_view(klass, view_config)
|
112
38
|
@klass_config.delete(klass)
|
113
|
-
@config[klass.to_s] =
|
39
|
+
@config[klass.to_s] = view_config
|
114
40
|
true
|
115
41
|
end
|
116
42
|
|
117
|
-
#
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
# This is the main method of this class. The formatter looks for the first helper in its config for the given output class.
|
123
|
-
# If a helper is found, the output is converted by the helper into a string and returned. If not, nil is returned. The options
|
124
|
-
# this class takes are a helper config hash as described in config. These options will be merged with any existing helper config hash
|
125
|
-
# an output class has in config. Any block given is passed along to a helper class.
|
43
|
+
# This method looks for an output object's view in Formatter.config and then Formatter.dynamic_config.
|
44
|
+
# If a view is found, a stringified view is returned based on the object. If no view is found, nil is returned. The options this
|
45
|
+
# class takes are a view hash as described in Formatter.config. These options will be merged with any existing helper
|
46
|
+
# config hash an output class has in Formatter.config. Any block given is passed along to a helper class.
|
126
47
|
def format_output(output, options={}, &block)
|
127
48
|
output_class = determine_output_class(output)
|
128
49
|
options = parse_console_options(options) if options.delete(:console)
|
129
50
|
options = Util.recursive_hash_merge(klass_config(output_class), options)
|
130
|
-
|
51
|
+
_format_output(output, options, &block)
|
52
|
+
end
|
53
|
+
|
54
|
+
#:stopdoc:
|
55
|
+
def _format_output(output, options, &block)
|
56
|
+
output = options[:output_method] ? (output.is_a?(Array) ?
|
57
|
+
output.map {|e| call_output_method(options[:output_method], e) } :
|
131
58
|
call_output_method(options[:output_method], output) ) : output
|
132
59
|
args = [output]
|
133
60
|
args << options[:options] if options[:options] && !options[:options].empty?
|
134
61
|
if options[:method]
|
135
|
-
|
136
|
-
elsif options[:class] && (helper_class =
|
137
|
-
|
62
|
+
send(options[:method],*args)
|
63
|
+
elsif options[:class] && (helper_class = Helpers.helper_class(options[:class]))
|
64
|
+
helper_class.render(*args, &block)
|
138
65
|
elsif options[:output_method]
|
139
|
-
|
66
|
+
output
|
140
67
|
end
|
141
|
-
new_output
|
142
68
|
end
|
143
69
|
|
144
|
-
#:stopdoc:
|
145
70
|
def parse_console_options(options) #:nodoc:
|
146
71
|
real_options = [:method, :class, :output_method].inject({}) do |h, e|
|
147
72
|
h[e] = options.delete(e) if options[e]; h
|
148
73
|
end
|
149
74
|
real_options.merge! :options=>options
|
150
|
-
real_options
|
151
|
-
end
|
152
|
-
|
153
|
-
def determine_helper_class(klass)
|
154
|
-
if (helper_class = Helpers.constants.find {|e| e.to_s == Util.camelize(klass.to_s)})
|
155
|
-
klass = "Hirb::Helpers::#{helper_class}"
|
156
|
-
end
|
157
|
-
Util.any_const_get(klass)
|
158
75
|
end
|
159
76
|
|
160
77
|
def determine_output_class(output)
|
@@ -168,28 +85,32 @@ module Hirb
|
|
168
85
|
# Internal view options built from user-defined ones. Options are built by recursively merging options from oldest
|
169
86
|
# ancestors to the most recent ones.
|
170
87
|
def klass_config(output_class)
|
171
|
-
@klass_config[output_class] ||=
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
88
|
+
@klass_config[output_class] ||= build_klass_config(output_class)
|
89
|
+
end
|
90
|
+
|
91
|
+
def build_klass_config(output_class)
|
92
|
+
output_ancestors = output_class.ancestors.map {|e| e.to_s}.reverse
|
93
|
+
output_ancestors.pop
|
94
|
+
hash = output_ancestors.inject({}) {|h, klass|
|
95
|
+
add_klass_config_if_true(h, klass) {|c,klass| c[klass] && c[klass][:ancestor] }
|
96
|
+
}
|
97
|
+
add_klass_config_if_true(hash, output_class.to_s) {|c,klass| c[klass] }
|
98
|
+
end
|
99
|
+
|
100
|
+
def add_klass_config_if_true(hash, klass)
|
101
|
+
if yield(@config, klass)
|
102
|
+
Util.recursive_hash_merge hash, @config[klass]
|
103
|
+
elsif yield(self.class.dynamic_config, klass)
|
104
|
+
@config[klass] = self.class.dynamic_config[klass].dup # copy to local
|
105
|
+
Util.recursive_hash_merge hash, self.class.dynamic_config[klass]
|
106
|
+
else
|
107
|
+
hash
|
176
108
|
end
|
177
109
|
end
|
178
110
|
|
179
111
|
def reset_klass_config
|
180
112
|
@klass_config = {}
|
181
113
|
end
|
182
|
-
|
183
|
-
def default_config
|
184
|
-
Views.constants.inject({}) {|h,e|
|
185
|
-
output_class = e.to_s.gsub("_", "::")
|
186
|
-
if (views_class = Views.const_get(e)) && views_class.respond_to?(:render)
|
187
|
-
default_options = views_class.respond_to?(:default_options) ? views_class.default_options : {}
|
188
|
-
h[output_class] = default_options.merge({:class=>"Hirb::Views::#{e}"})
|
189
|
-
end
|
190
|
-
h
|
191
|
-
}
|
192
|
-
end
|
193
114
|
#:startdoc:
|
194
115
|
end
|
195
|
-
end
|
116
|
+
end
|