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,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