hirber 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. checksums.yaml +7 -0
  2. data/.gemspec +21 -0
  3. data/.travis.yml +11 -0
  4. data/CHANGELOG.rdoc +165 -0
  5. data/CONTRIBUTING.md +1 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.rdoc +205 -0
  8. data/Rakefile +35 -0
  9. data/lib/bond/completions/hirb.rb +15 -0
  10. data/lib/hirb.rb +84 -0
  11. data/lib/hirb/console.rb +43 -0
  12. data/lib/hirb/dynamic_view.rb +113 -0
  13. data/lib/hirb/formatter.rb +126 -0
  14. data/lib/hirb/helpers.rb +18 -0
  15. data/lib/hirb/helpers/auto_table.rb +24 -0
  16. data/lib/hirb/helpers/markdown_table.rb +14 -0
  17. data/lib/hirb/helpers/object_table.rb +14 -0
  18. data/lib/hirb/helpers/parent_child_tree.rb +24 -0
  19. data/lib/hirb/helpers/tab_table.rb +24 -0
  20. data/lib/hirb/helpers/table.rb +376 -0
  21. data/lib/hirb/helpers/table/filters.rb +10 -0
  22. data/lib/hirb/helpers/table/resizer.rb +82 -0
  23. data/lib/hirb/helpers/tree.rb +181 -0
  24. data/lib/hirb/helpers/unicode_table.rb +15 -0
  25. data/lib/hirb/helpers/vertical_table.rb +37 -0
  26. data/lib/hirb/import_object.rb +10 -0
  27. data/lib/hirb/menu.rb +226 -0
  28. data/lib/hirb/pager.rb +106 -0
  29. data/lib/hirb/string.rb +44 -0
  30. data/lib/hirb/util.rb +96 -0
  31. data/lib/hirb/version.rb +3 -0
  32. data/lib/hirb/view.rb +272 -0
  33. data/lib/hirb/views.rb +8 -0
  34. data/lib/hirb/views/couch_db.rb +11 -0
  35. data/lib/hirb/views/misc_db.rb +15 -0
  36. data/lib/hirb/views/mongo_db.rb +17 -0
  37. data/lib/hirb/views/orm.rb +11 -0
  38. data/lib/hirb/views/rails.rb +19 -0
  39. data/lib/ripl/hirb.rb +15 -0
  40. data/test/auto_table_test.rb +33 -0
  41. data/test/console_test.rb +27 -0
  42. data/test/dynamic_view_test.rb +94 -0
  43. data/test/formatter_test.rb +176 -0
  44. data/test/hirb_test.rb +39 -0
  45. data/test/import_test.rb +9 -0
  46. data/test/menu_test.rb +272 -0
  47. data/test/object_table_test.rb +79 -0
  48. data/test/pager_test.rb +162 -0
  49. data/test/resizer_test.rb +62 -0
  50. data/test/table_test.rb +667 -0
  51. data/test/test_helper.rb +60 -0
  52. data/test/tree_test.rb +184 -0
  53. data/test/util_test.rb +59 -0
  54. data/test/view_test.rb +178 -0
  55. data/test/views_test.rb +22 -0
  56. metadata +164 -0
@@ -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,17 @@
1
+ module Hirb::Views::MongoDb #:nodoc:
2
+ def mongoid__document_view(obj)
3
+ fields = obj.class.fields.keys
4
+ fields.delete('_id')
5
+ fields.unshift('_id')
6
+ {:fields=>fields}
7
+ end
8
+
9
+ def mongo_mapper__document_view(obj)
10
+ fields = obj.class.column_names
11
+ fields.delete('_id') && fields.unshift('_id')
12
+ {:fields=>fields}
13
+ end
14
+ alias_method :mongo_mapper__embedded_document_view, :mongo_mapper__document_view
15
+ end
16
+
17
+ 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
@@ -0,0 +1,19 @@
1
+ module Hirb::Views::Rails #:nodoc:
2
+ def active_record__base_view(obj)
3
+ {:fields=>get_active_record_fields(obj)}
4
+ end
5
+
6
+ def get_active_record_fields(obj)
7
+ fields = obj.class.column_names.map {|e| e.to_sym }
8
+ # if query used select
9
+ if obj.attributes.keys.compact.sort != obj.class.column_names.sort
10
+ selected_columns = obj.attributes.keys.compact
11
+ sorted_columns = obj.class.column_names.dup.delete_if {|e| !selected_columns.include?(e) }
12
+ sorted_columns += (selected_columns - sorted_columns)
13
+ fields = sorted_columns.map {|e| e.to_sym}
14
+ end
15
+ fields
16
+ end
17
+ end
18
+
19
+ Hirb::DynamicView.add Hirb::Views::Rails, :helper=>:auto_table
@@ -0,0 +1,15 @@
1
+ require 'hirb'
2
+
3
+ module Ripl::Hirb
4
+ def before_loop
5
+ super
6
+ Hirb.enable(Ripl.config[:hirb] || {})
7
+ end
8
+
9
+ def format_result(result)
10
+ return super if !Hirb::View.enabled?
11
+ Hirb::View.view_or_page_output(result) || super
12
+ end
13
+ end
14
+
15
+ Ripl::Shell.include Ripl::Hirb
@@ -0,0 +1,33 @@
1
+ require File.join(File.dirname(__FILE__), 'test_helper')
2
+
3
+ describe "auto table" do
4
+ it "converts nonarrays to arrays and renders" do
5
+ require 'set'
6
+ # rubinius sorts Set#to_a differently
7
+ arr = RUBY_DESCRIPTION.include?('rubinius') ? Set.new([1,2,3]).to_a : [1,2,3]
8
+
9
+ expected_table = <<-TABLE.unindent
10
+ +-------+
11
+ | value |
12
+ +-------+
13
+ | #{arr[0]} |
14
+ | #{arr[1]} |
15
+ | #{arr[2]} |
16
+ +-------+
17
+ 3 rows in set
18
+ TABLE
19
+ Helpers::AutoTable.render(::Set.new([1,2,3])).should == expected_table
20
+ end
21
+
22
+ it "renders hash" do
23
+ expected_table = <<-TABLE.unindent
24
+ +---+-------+
25
+ | 0 | 1 |
26
+ +---+-------+
27
+ | a | 12345 |
28
+ +---+-------+
29
+ 1 row in set
30
+ TABLE
31
+ Helpers::AutoTable.render({:a=>12345}).should == expected_table
32
+ end
33
+ end
@@ -0,0 +1,27 @@
1
+ require File.join(File.dirname(__FILE__), 'test_helper')
2
+
3
+ describe "Console" do
4
+ it "#table is called without Hirb enabled" do
5
+ extend Hirb::Console
6
+ reset_config
7
+ expected_table = <<-TABLE.unindent
8
+ +-------+
9
+ | value |
10
+ +-------+
11
+ | 5 |
12
+ | 3 |
13
+ +-------+
14
+ 2 rows in set
15
+ TABLE
16
+ capture_stdout {
17
+ table([5,3], :fields=>[:to_s])
18
+ }.should == expected_table +"\n"
19
+ end
20
+
21
+ it ".render_output sets config if it wasn't before" do
22
+ reset_config
23
+ View.expects(:render_output)
24
+ Console.render_output('blah')
25
+ View.config.is_a?(Hash).should == true
26
+ end
27
+ end
@@ -0,0 +1,94 @@
1
+ require File.join(File.dirname(__FILE__), 'test_helper')
2
+
3
+ describe "DynamicView" do
4
+ def output_expects(output, expects)
5
+ Helpers::ObjectTable.expects(:render).with(output, expects)
6
+ Helpers::AutoTable.render(output)
7
+ end
8
+
9
+ describe "add" do
10
+ before_all { View.load_config }
11
+
12
+ it "raises error if no :helper option" do
13
+ lambda { Hirb.add_dynamic_view 'Blah', {} }.should.raise(ArgumentError).
14
+ message.should =~ /:helper.*required/
15
+ end
16
+
17
+ it "raises error if :helper option not a dynamic_view module" do
18
+ lambda { Hirb.add_dynamic_view('Blah', :helper=>:table) {|obj| } }.
19
+ should.raise(ArgumentError).message.should =~ /:helper.*must/
20
+ end
21
+
22
+ it "raises error if views module not a module" do
23
+ lambda { Hirb.add_dynamic_view 'Blah', :helper=>:auto_table }.should.raise(ArgumentError).
24
+ message.should =~ /must be a module/
25
+ end
26
+
27
+ it "adds a view with block" do
28
+ Hirb.add_dynamic_view('Date', :helper=>:auto_table) do |obj|
29
+ {:fields=>obj.class::DAYNAMES}
30
+ end
31
+ output_expects [Date.new], :fields=>Date::DAYNAMES
32
+ end
33
+
34
+ it "when adding views with a block, second view for same class overrides first one" do
35
+ Hirb.add_dynamic_view('Date', :helper=>:auto_table) do |obj|
36
+ {:fields=>obj.class::DAYNAMES}
37
+ end
38
+ Hirb.add_dynamic_view('Date', :helper=>:auto_table) do |obj|
39
+ {:fields=>[:blah]}
40
+ end
41
+ output_expects [Date.new], :fields=>[:blah]
42
+ end
43
+ end
44
+
45
+ it "class_to_method and method_to_class convert to each other" do
46
+ ["DBI::Row", "Hirb::View"].each do |e|
47
+ Helpers::AutoTable.method_to_class(DynamicView.class_to_method(e).downcase).should == e
48
+ end
49
+ end
50
+
51
+ it "class_to_method converts correctly" do
52
+ DynamicView.class_to_method("DBI::Row").should == 'd_b_i__row_view'
53
+ end
54
+
55
+ describe "dynamic_view" do
56
+ def define_view(mod_name= :Blah, &block)
57
+ mod = Views.const_set(mod_name, Module.new)
58
+ mod_block = block_given? ? block : lambda {|obj| {:fields=>obj.class::DAYNAMES}}
59
+ mod.send(:define_method, :date_view, mod_block)
60
+ Hirb.add_dynamic_view mod, :helper=>:auto_table
61
+ end
62
+
63
+ before_all { View.load_config }
64
+ before { Formatter.dynamic_config = {} }
65
+ after { Views.send(:remove_const, :Blah) }
66
+
67
+ it "sets a view's options" do
68
+ define_view
69
+ output_expects [Date.new], :fields=>Date::DAYNAMES
70
+ end
71
+
72
+ it "does override existing formatter dynamic_config" do
73
+ Formatter.dynamic_config["Date"] = {:class=>Helpers::Table}
74
+ define_view
75
+ Formatter.dynamic_config["Date"].should == {:class=>Hirb::Helpers::AutoTable, :ancestor=>true}
76
+ end
77
+
78
+ it "raises a readable error when error occurs in a view" do
79
+ define_view {|obj| raise 'blah' }
80
+ lambda { Helpers::AutoTable.render([Date.new]) }.should.raise(RuntimeError).
81
+ message.should =~ /'Date'.*date_view.*\nblah/
82
+ end
83
+
84
+ it "another view can reuse an old view's options" do
85
+ define_view
86
+ define_view(:Blah2) do |obj|
87
+ {:fields=>obj.class::DAYNAMES + ['blah']}
88
+ end
89
+ output_expects [Date.new], :fields=>(Date::DAYNAMES + ['blah'])
90
+ end
91
+ after_all { reset_config }
92
+ end
93
+ after_all { Formatter.dynamic_config = {} }
94
+ end
@@ -0,0 +1,176 @@
1
+ require File.join(File.dirname(__FILE__), 'test_helper')
2
+
3
+ describe "Formatter" do
4
+ def set_formatter(hash={})
5
+ @formatter = Formatter.new(hash)
6
+ end
7
+
8
+ describe "klass_config" do
9
+ it "recursively merges ancestor options" do
10
+ @formatter = set_formatter "String"=>{:args=>[1,2], :options=>{:fields=>[:to_s]}},
11
+ "Object"=>{:method=>:object_output, :ancestor=>true, :options=>{:vertical=>true}},
12
+ "Kernel"=>{:method=>:default_output}
13
+ expected_result = {:method=>:object_output, :args=>[1, 2], :ancestor=>true, :options=>{:fields=>[:to_s], :vertical=>true}}
14
+ @formatter.klass_config(::String).should == expected_result
15
+ end
16
+
17
+ it "doesn't merge ancestor options" do
18
+ @formatter = set_formatter "String"=>{:args=>[1,2]}, "Object"=>{:method=>:object_output},
19
+ "Kernel"=>{:method=>:default_output}
20
+ @formatter.klass_config(::String).should == {:args=>[1, 2]}
21
+ end
22
+
23
+ it "returns hash when nothing found" do
24
+ set_formatter.klass_config(::String).should == {}
25
+ end
26
+
27
+ describe "with dynamic_config" do
28
+ def set_formatter(hash={})
29
+ @formatter = Formatter.new(hash)
30
+ end
31
+ after { Formatter.dynamic_config = {}}
32
+
33
+ it "merges ancestor options and sets local config" do
34
+ Formatter.dynamic_config = {"Object"=>{:method=>:blah}, "Kernel"=>{:args=>[1,2], :ancestor=>true}}
35
+ set_formatter.klass_config(::String).should == {:args=>[1,2], :ancestor=>true}
36
+ @formatter.config['Kernel'].should == {:args=>[1,2], :ancestor=>true}
37
+ end
38
+
39
+ it "uses local config over dynamic_config" do
40
+ Formatter.dynamic_config = {"String"=>{:method=>:blah}}
41
+ set_formatter "String"=>{:args=>[1,2]}
42
+ @formatter.klass_config(::String).should == {:args=>[1,2]}
43
+ end
44
+
45
+ it "uses dynamic_config and sets local config" do
46
+ Formatter.dynamic_config = {"String"=>{:method=>:blah}}
47
+ set_formatter.klass_config(::String).should == {:method=>:blah}
48
+ @formatter.config['String'].should == {:method=>:blah}
49
+ end
50
+ end
51
+ end
52
+
53
+ describe "formatter methods:" do
54
+ before_all { eval "module ::Dooda; end" }
55
+
56
+ it "#add_view sets formatter config" do
57
+ @formatter = set_formatter
58
+ @formatter.add_view ::Dooda, :class=>"DoodaView"
59
+ @formatter.klass_config(::Dooda).should == {:class=>"DoodaView"}
60
+ end
61
+
62
+ it "#add_view overwrites existing formatter config" do
63
+ @formatter = set_formatter "Dooda"=>{:class=>"DoodaView"}
64
+ @formatter.add_view ::Dooda, :class=>"DoodaView2"
65
+ @formatter.klass_config(::Dooda).should == {:class=>"DoodaView2"}
66
+ end
67
+
68
+ it "#parse_console_options passes all options except for formatter options into :options" do
69
+ @formatter = set_formatter
70
+ options = {:class=>'blah', :method=>'blah', :output_method=>'blah', :blah=>'blah'}
71
+ expected_options = {:class=>'blah', :method=>'blah', :output_method=>'blah', :options=>{:blah=>'blah'}}
72
+ @formatter.parse_console_options(options).should == expected_options
73
+ end
74
+
75
+ it "#determine_output_class recognizes subclasses of to_a classes" do
76
+ class Array2 < Array; end
77
+ @formatter.determine_output_class(Array2.new(%w{ok dude})).should == String
78
+ end
79
+ end
80
+
81
+ describe "format_output" do
82
+ def view_output(*args, &block); View.view_output(*args, &block); end
83
+ def render_method(*args); View.render_method(*args); end
84
+
85
+ def enable_with_output(value)
86
+ Hirb.enable :output=>value
87
+ end
88
+
89
+ before_all {
90
+ eval %[module ::Commify
91
+ def self.render(strings)
92
+ strings = Array(strings)
93
+ strings.map {|e| e.split('').join(',')}.join("\n")
94
+ end
95
+ end]
96
+ reset_config
97
+ }
98
+ before { View.formatter = nil; reset_config }
99
+ after { Hirb.disable }
100
+
101
+ it "formats with method option" do
102
+ eval "module ::Kernel; def commify(string); string.split('').join(','); end; end"
103
+ enable_with_output "String"=>{:method=>:commify}
104
+ render_method.expects(:call).with('d,u,d,e')
105
+ view_output('dude')
106
+ end
107
+
108
+ it "formats with class option" do
109
+ enable_with_output "String"=>{:class=>"Commify"}
110
+ render_method.expects(:call).with('d,u,d,e')
111
+ view_output('dude')
112
+ end
113
+
114
+ it "formats with class option as symbol" do
115
+ enable_with_output "String"=>{:class=>:auto_table}
116
+ Helpers::AutoTable.expects(:render)
117
+ view_output('dude')
118
+ end
119
+
120
+ it "formats arrays" do
121
+ enable_with_output "String"=>{:class=>"Commify"}
122
+ render_method.expects(:call).with('d,u,d,e')
123
+ view_output(['dude'])
124
+ end
125
+
126
+ it "formats array-like objects" do
127
+ enable_with_output "String"=>{:class=>"Commify"}
128
+ render_method.expects(:call).with('d,u,d,e')
129
+ require 'set'
130
+ view_output Set.new(['dude'])
131
+ end
132
+
133
+ it "formats with options option" do
134
+ eval "module ::Blahify; def self.render(*args); end; end"
135
+ enable_with_output "String"=>{:class=>"Blahify", :options=>{:fields=>%w{a b}}}
136
+ Blahify.expects(:render).with('dude', :fields=>%w{a b})
137
+ view_output('dude')
138
+ end
139
+
140
+ it "doesn't format and returns false when no format method found" do
141
+ Hirb.enable
142
+ render_method.expects(:call).never
143
+ view_output(Date.today).should == false
144
+ end
145
+
146
+ it "formats with output_method option as method" do
147
+ enable_with_output 'String'=>{:class=>"Commify", :output_method=>:chop}
148
+ render_method.expects(:call).with('d,u,d')
149
+ view_output('dude')
150
+ end
151
+
152
+ it "formats with output_method option as proc" do
153
+ enable_with_output 'String'=>{:class=>"Commify", :output_method=>lambda {|e| e.chop}}
154
+ render_method.expects(:call).with('d,u,d')
155
+ view_output('dude')
156
+ end
157
+
158
+ it "formats output array with output_method option" do
159
+ enable_with_output 'String'=>{:class=>"Commify", :output_method=>:chop}
160
+ render_method.expects(:call).with("d,u,d\nm,a")
161
+ view_output(['dude', 'man'])
162
+ end
163
+
164
+ it "formats with explicit class option" do
165
+ enable_with_output 'String'=>{:class=>"Blahify"}
166
+ render_method.expects(:call).with('d,u,d,e')
167
+ view_output('dude', :class=>"Commify")
168
+ end
169
+
170
+ it "formats with explicit options option merges with existing options" do
171
+ enable_with_output "String"=>{:class=>"Commify", :options=>{:fields=>%w{f1 f2}}}
172
+ Commify.expects(:render).with('dude', :max_width=>10, :fields=>%w{f1 f2})
173
+ view_output('dude', :options=>{:max_width=>10})
174
+ end
175
+ end
176
+ end
@@ -0,0 +1,39 @@
1
+ require File.join(File.dirname(__FILE__), 'test_helper')
2
+
3
+ describe "Hirb" do
4
+ before_all { Hirb.config_files = nil }
5
+ before { Hirb.config = nil }
6
+
7
+ it "config converts yaml when config file exists" do
8
+ yaml_data = {:blah=>'blah'}
9
+ File.stubs('exist?').returns(true)
10
+ Hirb.config_files = ['ok']
11
+ YAML.expects(:load_file).returns(yaml_data)
12
+ Hirb.config.should == yaml_data
13
+ end
14
+
15
+ it "config defaults to hash when no config file" do
16
+ File.stubs('exist?').returns(false)
17
+ Hirb.config.should == {}
18
+ end
19
+
20
+ it "config reloads if given explicit reload" do
21
+ Hirb.config
22
+ Hirb.expects(:read_config_file).returns({})
23
+ Hirb.config(true)
24
+ end
25
+
26
+ it "config reads multiple config files and merges them" do
27
+ Hirb.config_files = %w{one two}
28
+ Hirb.expects(:read_config_file).times(2).returns({:output=>{"String"=>:auto_table}}, {:output=>{"Array"=>:auto_table}})
29
+ Hirb.config.should == {:output=>{"Array"=>:auto_table, "String"=>:auto_table}}
30
+ Hirb.config_files = nil
31
+ end
32
+
33
+ it "config_file sets correctly when no ENV['HOME']" do
34
+ Hirb.config_files = nil
35
+ home = ENV.delete('HOME')
36
+ Hirb.config_files[0].class.should == String
37
+ ENV["HOME"] = home
38
+ end
39
+ end