hirber 0.8.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.
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