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,60 @@
1
+ require 'bacon'
2
+ require 'bacon/bits'
3
+ require 'mocha-on-bacon'
4
+ require 'hirb'
5
+ include Hirb
6
+
7
+ module TestHelpers
8
+ # set these to avoid invoking stty multiple times which doubles test suite running time
9
+ ENV["LINES"] = ENV["COLUMNS"] = "20"
10
+ def reset_terminal_size
11
+ ENV["LINES"] = ENV["COLUMNS"] = "20"
12
+ end
13
+
14
+ def capture_stdout(&block)
15
+ original_stdout = $stdout
16
+ $stdout = fake = StringIO.new
17
+ begin
18
+ yield
19
+ ensure
20
+ $stdout = original_stdout
21
+ end
22
+ fake.string
23
+ end
24
+
25
+ def capture_stderr(&block)
26
+ original_stderr = $stderr
27
+ $stderr = fake = StringIO.new
28
+ begin
29
+ yield
30
+ ensure
31
+ $stderr = original_stderr
32
+ end
33
+ fake.string
34
+ end
35
+
36
+ def reset_config
37
+ View.instance_eval "@config = nil"
38
+ end
39
+ end
40
+
41
+ class Bacon::Context
42
+ include TestHelpers
43
+ end
44
+
45
+ class String
46
+ def unindent(num=nil)
47
+ regex = num ? /^\s{#{num}}/ : /^\s*/
48
+ gsub(regex, '').chomp
49
+ end
50
+ end
51
+
52
+ # mocks IRB for View + Pager
53
+ module ::IRB
54
+ class Irb
55
+ def initialize(context)
56
+ @context = context
57
+ end
58
+ def output_value; end
59
+ end
60
+ end
@@ -0,0 +1,184 @@
1
+ require File.join(File.dirname(__FILE__), 'test_helper')
2
+
3
+ describe "Tree helpers:" do
4
+ def tree(*args)
5
+ Helpers::Tree.render(*args)
6
+ end
7
+
8
+ describe "basic tree" do
9
+ it "with hash nodes renders" do
10
+ expected_tree = <<-TREE.unindent(6)
11
+ 0.0
12
+ 1.1
13
+ 2.2
14
+ 3.2
15
+ 4.1
16
+ TREE
17
+ tree([{:level=>0, :value=>'0.0'}, {:level=>1, :value=>'1.1'}, {:level=>2, :value=>'2.2'},{:level=>2, :value=>'3.2'},
18
+ {:level=>1, :value=>'4.1'}]).should == expected_tree
19
+ end
20
+
21
+ it "with array nodes renders" do
22
+ expected_tree = <<-TREE.unindent(6)
23
+ 0.0
24
+ 1.1
25
+ 2.2
26
+ 3.2
27
+ 4.1
28
+ TREE
29
+ tree([[0, "0.0"], [1, "1.1"], [2, "2.2"], [2, "3.2"], [1, "4.1"]]).should == expected_tree
30
+ end
31
+
32
+ it "with non-string values renders" do
33
+ expected_tree = <<-TREE.unindent(6)
34
+ 0.0
35
+ 1.1
36
+ 2.2
37
+ 3.2
38
+ 4.1
39
+ TREE
40
+ tree([[0,0.0],[1,1.1],[2,2.2],[2,3.2],[1,4.1]]).should == expected_tree
41
+ end
42
+
43
+ it "with indent option renders" do
44
+ expected_tree = <<-TREE.unindent(6)
45
+ 0.0
46
+ 1.1
47
+ 2.2
48
+ 3.2
49
+ 4.1
50
+ TREE
51
+ tree([[0, "0.0"], [1, "1.1"], [2, "2.2"], [2, "3.2"], [1, "4.1"]], :indent=>2).should == expected_tree
52
+ end
53
+
54
+ it "with limit option renders" do
55
+ expected_tree = <<-TREE.unindent(6)
56
+ 0.0
57
+ 1.1
58
+ 4.1
59
+ TREE
60
+ tree([[0, "0.0"], [1, "1.1"], [2, "2.2"], [2, "3.2"], [1, "4.1"]], :limit=>1).should == expected_tree
61
+ end
62
+
63
+ it "with description option renders" do
64
+ expected_tree = <<-TREE.unindent(6)
65
+ 0.0
66
+ 1.1
67
+ 2.2
68
+ 3.2
69
+ 4.1
70
+
71
+ 5 nodes in tree
72
+ TREE
73
+ tree([[0, "0.0"], [1, "1.1"], [2, "2.2"], [2, "3.2"], [1, "4.1"]], :description=>true).should == expected_tree
74
+ end
75
+
76
+ it "with type directory renders" do
77
+ expected_tree = <<-TREE.unindent
78
+ 0.0
79
+ |-- 1.1
80
+ | |-- 2.2
81
+ | `-- 3.2
82
+ `-- 4.1
83
+ TREE
84
+ tree([[0, "0.0"], [1, "1.1"], [2, "2.2"], [2, "3.2"], [1, "4.1"]], :type=>:directory).should == expected_tree
85
+ end
86
+
87
+ it "with type directory and multiple children per level renders" do
88
+ expected_tree = <<-TREE.unindent
89
+ 0.0
90
+ |-- 1.1
91
+ | |-- 2.2
92
+ | | `-- 3.3
93
+ | `-- 4.2
94
+ | `-- 5.3
95
+ `-- 6.1
96
+ TREE
97
+ tree([[0,'0.0'], [1,'1.1'], [2,'2.2'],[3,'3.3'],[2,'4.2'],[3,'5.3'],[1,'6.1']], :type=>:directory).should == expected_tree
98
+ end
99
+
100
+ it "with type number renders" do
101
+ expected_tree = <<-TREE.unindent(6)
102
+ 1. 0
103
+ 1. 1
104
+ 1. 2
105
+ 2. 3
106
+ 2. 4
107
+ TREE
108
+ tree([[0,'0'],[1,'1'],[2,'2'],[2,'3'],[1,'4']], :type=>:number).should == expected_tree
109
+ end
110
+
111
+ it "with multi-line nodes option renders" do
112
+ expected_tree = <<-TREE.unindent(6)
113
+ parent
114
+ +-------+
115
+ | value |
116
+ +-------+
117
+ | 1 |
118
+ | 2 |
119
+ | 3 |
120
+ +-------+
121
+ indented
122
+ stuff
123
+ TREE
124
+ node1 = "+-------+\n| value |\n+-------+\n| 1 |\n| 2 |\n| 3 |\n+-------+"
125
+ tree([ [0, 'parent'],[1, node1],[2, "indented\nstuff"]], :multi_line_nodes=>true).should == expected_tree
126
+ end
127
+ end
128
+
129
+ def mock_node(value, value_method)
130
+ children = []
131
+ value,children = *value if value.is_a?(Array)
132
+ mock(value_method=>value, :children=>children.map {|e| mock_node(e, value_method)})
133
+ end
134
+
135
+ describe "parent_child_tree" do
136
+ it "with name value renders" do
137
+ expected_tree = <<-TREE.unindent
138
+ 0.0
139
+ |-- 1.1
140
+ |-- 2.1
141
+ | `-- 3.2
142
+ `-- 4.1
143
+ TREE
144
+ root = mock_node(['0.0', ['1.1', ['2.1', ['3.2']], '4.1']], :name)
145
+ Helpers::ParentChildTree.render(root, :type=>:directory).should == expected_tree
146
+ end
147
+
148
+ it "with value_method option renders" do
149
+ expected_tree = <<-TREE.unindent
150
+ 0.0
151
+ |-- 1.1
152
+ |-- 2.1
153
+ | `-- 3.2
154
+ `-- 4.1
155
+ TREE
156
+ root = mock_node(['0.0', ['1.1', ['2.1', ['3.2']], '4.1']], :blah)
157
+ Helpers::ParentChildTree.render(root, :type=>:directory, :value_method=>:blah).should == expected_tree
158
+ end
159
+
160
+ it "with children_method proc option renders" do
161
+ expected_tree = <<-TREE.unindent
162
+ 1
163
+ |-- 2
164
+ |-- 3
165
+ |-- 4
166
+ `-- 5
167
+ TREE
168
+ Helpers::ParentChildTree.render(1, :type=>:directory,
169
+ :children_method=>lambda {|e| e == 1 ? (2..5).to_a : []}, :value_method=>:to_s).should == expected_tree
170
+ end
171
+ end
172
+
173
+ it "tree with parentless nodes renders ParentlessNodeError" do
174
+ lambda { tree([[0, "0.0"], [2, '1.2']], :validate=>true) }.should.raise(Helpers::Tree::ParentlessNodeError)
175
+ end
176
+
177
+ it "tree with hash nodes missing level raises MissingLevelError" do
178
+ lambda { tree([{:value=>'ok'}]) }.should.raise(Helpers::Tree::Node::MissingLevelError)
179
+ end
180
+
181
+ it "tree with hash nodes missing level raises MissingValueError" do
182
+ lambda { tree([{:level=>0}]) }.should.raise(Helpers::Tree::Node::MissingValueError)
183
+ end
184
+ end
@@ -0,0 +1,59 @@
1
+ require File.join(File.dirname(__FILE__), 'test_helper')
2
+
3
+ describe "Util" do
4
+ it "camelize converts underscore lowercase to camelcase" do
5
+ Util.camelize('hirb/util').should == "Hirb::Util"
6
+ Util.camelize('hirb_hash').should == "HirbHash"
7
+ end
8
+
9
+ it "any_const_get returns nested class" do
10
+ Util.any_const_get("Hirb::Helpers").should == Hirb::Helpers
11
+ end
12
+
13
+ it "any_const_get returns nil for invalid class" do
14
+ Util.any_const_get("Basdfr").should == nil
15
+ end
16
+
17
+ it "any_const_get returns class when given class" do
18
+ Util.any_const_get(String).should == String
19
+ end
20
+
21
+ it "recursive_hash_merge merges" do
22
+ expected_hash = {:output=>{:fields=>["f1", "f2"], :method=>"blah"}, :key1=>"hash1", :key2=>"hash2"}
23
+ Util.recursive_hash_merge({:output=>{:fields=>%w{f1 f2}}, :key1=>'hash1'},
24
+ {:output=>{:method=>'blah'}, :key2=>'hash2'}).should == expected_hash
25
+ end
26
+
27
+ it "choose_from_array specifies range with -" do
28
+ Util.choose_from_array([1,2,3,4], '1-2,4').should == [1,2,4]
29
+ end
30
+
31
+ it "choose_from_array specifies range with .." do
32
+ Util.choose_from_array([1,2,3,4], '1 .. 2,4').should == [1,2,4]
33
+ end
34
+
35
+ it "choose_from_array chooses all with *" do
36
+ Util.choose_from_array([1,2,3,4], '*').should == [1,2,3,4]
37
+ end
38
+
39
+ it "choose_from_array ignores non-numerical input" do
40
+ Util.choose_from_array([1,2,3,4], 'a,2').should == [2]
41
+ end
42
+
43
+ it "choose_from_array ignores 0" do
44
+ Util.choose_from_array([1,2,3,4], '0,2').should == [2]
45
+ end
46
+
47
+ it "choose_from_array returns empty when empty input" do
48
+ Util.choose_from_array([1,2,3,4], "\n").should == []
49
+ end
50
+
51
+ it "choose_from_array returns empty with an invalid range" do
52
+ Util.choose_from_array([1,2,3,4], "5").should == []
53
+ end
54
+
55
+ it "capture_stdout" do
56
+ string = "sweetness man"
57
+ Util.capture_stdout { puts string }.should == string + "\n"
58
+ end
59
+ end
@@ -0,0 +1,178 @@
1
+ require File.join(File.dirname(__FILE__), 'test_helper')
2
+
3
+ describe "View" do
4
+ def formatter_config
5
+ View.formatter.config
6
+ end
7
+
8
+ it "page_output pages when view is enabled" do
9
+ Hirb.enable
10
+ View.pager.stubs(:activated_by?).returns(true)
11
+ View.pager.expects(:page)
12
+ View.page_output('blah').should == true
13
+ Hirb.disable
14
+ end
15
+
16
+ it "page_output doesn't page when view is disabled" do
17
+ Hirb.enable
18
+ Hirb.disable
19
+ View.pager.stubs(:activated_by?).returns(true)
20
+ View.pager.expects(:page).never
21
+ View.page_output('blah').should == false
22
+ end
23
+
24
+ it "view_output catches unexpected errors and prints them" do
25
+ reset_config
26
+ Hirb.enable
27
+ View.expects(:render_output).raises('blah')
28
+ capture_stderr { View.view_output([1,2,3]) }.should =~ /Hirb Error: blah/
29
+ Hirb.disable
30
+ end
31
+
32
+ describe "enable" do
33
+ before { reset_config }
34
+ after { Hirb.disable }
35
+ it "redefines irb output_value" do
36
+ View.expects(:render_output).once
37
+ Hirb.enable
38
+ context_stub = stub(:last_value=>'')
39
+ ::IRB::Irb.new(context_stub).output_value
40
+ end
41
+
42
+ it "is enabled?" do
43
+ Hirb.enable
44
+ View.enabled?.should == true
45
+ end
46
+
47
+ def output_class_config(klass)
48
+ { :output=>{klass=>{:class=>:auto_table}} }
49
+ end
50
+
51
+ it "sets formatter config" do
52
+ class_hash = {"Something::Base"=>{:class=>"BlahBlah"}}
53
+ Hirb.enable :output=>class_hash
54
+ View.formatter_config['Something::Base'].should == class_hash['Something::Base']
55
+ end
56
+
57
+ it "when called multiple times merges configs" do
58
+ Hirb.config = nil
59
+ # default config + config_file
60
+ Hirb.expects(:read_config_file).returns(output_class_config('Regexp'))
61
+ Hirb.enable output_class_config('String')
62
+
63
+ # add config file and explicit config
64
+ [{:config_file=>'ok'}, output_class_config('Struct')].each do |config|
65
+ Hirb.expects(:read_config_file).times(2).returns(
66
+ output_class_config('ActiveRecord::Base'), output_class_config('Array'))
67
+ Hirb.enable config
68
+ end
69
+
70
+ Hirb.config_files.include?('ok').should == true
71
+ output_keys = %w{ActiveRecord::Base Array Regexp String Struct}
72
+ View.config[:output].keys.sort.should == output_keys
73
+ end
74
+
75
+ it "when called multiple times without config doesn't affect config" do
76
+ Hirb.enable
77
+ old_config = View.config
78
+ Hirb.expects(:read_config_file).never
79
+ View.expects(:load_config).never
80
+ Hirb.enable
81
+ View.config.should == old_config
82
+ end
83
+
84
+ it "works without irb" do
85
+ Object.stubs(:const_defined?).with(:IRB).returns(false)
86
+ Hirb.enable
87
+ formatter_config.size.should.be > 0
88
+ end
89
+
90
+ it "with config_file option adds to config_file" do
91
+ Hirb.enable :config_file=> 'test_file'
92
+ Hirb.config_files.include?('test_file').should == true
93
+ end
94
+
95
+ it "with ignore_errors enable option" do
96
+ Hirb.enable :ignore_errors => true
97
+ View.stubs(:render_output).raises(Exception, "Ex mesg")
98
+ capture_stderr { View.view_output("").should == false }.should =~ /Error: Ex mesg/
99
+ end
100
+ end
101
+
102
+ describe "resize" do
103
+ def pager; View.pager; end
104
+ before do
105
+ View.pager = nil; reset_config; Hirb.enable
106
+ end
107
+
108
+ after { Hirb.disable}
109
+ it "changes width and height with stty" do
110
+ if RUBY_PLATFORM[/java/]
111
+ Util.expects(:command_exists?).with('tput').returns(false)
112
+ end
113
+ # stub tty? since running with rake sets
114
+ STDIN.stubs(:tty?).returns(true)
115
+ Util.expects(:command_exists?).with('stty').returns(true)
116
+ ENV['COLUMNS'] = ENV['LINES'] = nil # bypasses env usage
117
+
118
+ capture_stderr { View.resize }
119
+
120
+ pager.width.should.not == 10
121
+ pager.height.should.not == 10
122
+ reset_terminal_size
123
+ end
124
+
125
+ it "changes width and height with ENV" do
126
+ ENV['COLUMNS'] = ENV['LINES'] = '10' # simulates resizing
127
+ View.resize
128
+ pager.width.should == 10
129
+ pager.height.should == 10
130
+ end
131
+
132
+ it "with no environment or stty still has valid width and height" do
133
+ View.config[:width] = View.config[:height] = nil
134
+ unless RUBY_PLATFORM[/java/]
135
+ Util.expects(:command_exists?).with('stty').returns(false)
136
+ end
137
+ ENV['COLUMNS'] = ENV['LINES'] = nil
138
+
139
+ View.resize
140
+ pager.width.is_a?(Integer).should == true
141
+ pager.height.is_a?(Integer).should == true
142
+ reset_terminal_size
143
+ end
144
+ end
145
+
146
+ it "disable points output_value back to original output_value" do
147
+ View.expects(:render_output).never
148
+ Hirb.enable
149
+ Hirb.disable
150
+ context_stub = stub(:last_value=>'')
151
+ ::IRB::Irb.new(context_stub).output_value
152
+ end
153
+
154
+ it "disable works without irb defined" do
155
+ Object.stubs(:const_defined?).with(:IRB).returns(false)
156
+ Hirb.enable
157
+ Hirb.disable
158
+ View.enabled?.should == false
159
+ end
160
+
161
+ it "capture_and_render" do
162
+ string = 'no waaaay'
163
+ View.render_method.expects(:call).with(string)
164
+ View.capture_and_render { print string }
165
+ end
166
+
167
+ it "state is toggled by toggle_pager" do
168
+ previous_state = View.config[:pager]
169
+ View.toggle_pager
170
+ View.config[:pager].should == !previous_state
171
+ end
172
+
173
+ it "state is toggled by toggle_formatter" do
174
+ previous_state = View.config[:formatter]
175
+ View.toggle_formatter
176
+ View.config[:formatter].should == !previous_state
177
+ end
178
+ end