debug-bar 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +6 -0
- data/.rbenv-version +1 -0
- data/.rspec +1 -0
- data/Gemfile +16 -0
- data/README.rdoc +192 -0
- data/Rakefile +27 -0
- data/debug-bar.gemspec +24 -0
- data/lib/debug-bar/base.rb +208 -0
- data/lib/debug-bar/default.rb +26 -0
- data/lib/debug-bar/ext/binding.rb +17 -0
- data/lib/debug-bar/ext/object.rb +20 -0
- data/lib/debug-bar/ext/string.rb +18 -0
- data/lib/debug-bar/ext.rb +3 -0
- data/lib/debug-bar/recipe_book/base.rb +104 -0
- data/lib/debug-bar/recipe_book/default.rb +34 -0
- data/lib/debug-bar/recipe_book.rb +2 -0
- data/lib/debug-bar/version.rb +3 -0
- data/lib/debug-bar.rb +6 -0
- data/lib/templates/callback_box.html.erb +12 -0
- data/lib/templates/error.html.erb +6 -0
- data/lib/templates/layout.html.erb +142 -0
- data/spec/base_spec.rb +363 -0
- data/spec/binding_spec.rb +62 -0
- data/spec/recipe_book_spec.rb +161 -0
- data/spec/spec_helper.rb +19 -0
- data/spec/support/templates/basic.html.erb +1 -0
- data/spec/support/templates/content.html.erb +1 -0
- metadata +116 -0
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'pp'
|
2
|
+
require 'awesome_print'
|
3
|
+
require 'cgi'
|
4
|
+
|
5
|
+
module DebugBar
|
6
|
+
module RecipeBook
|
7
|
+
# A default RecipeBook with recipes useful for Rails applications.
|
8
|
+
class Default < Base
|
9
|
+
|
10
|
+
# Displays params in a user readable fashion.
|
11
|
+
#
|
12
|
+
# If the :cutoff option is given, it auto-hides when the params are
|
13
|
+
# more characters in length than the cutoff, otherwise it defaults to
|
14
|
+
# a sane length.
|
15
|
+
def params_recipe(opts={})
|
16
|
+
return Proc.new do |b|
|
17
|
+
params_s = b[:params].awesome_print_html
|
18
|
+
['Params', params_s, {:id => 'params'}]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# Displays the session in a pretty printed way.
|
23
|
+
def session_recipe
|
24
|
+
return Proc.new {|b| ['Session', b[:session].awesome_print_html, {:id => 'session'}]}
|
25
|
+
end
|
26
|
+
|
27
|
+
# Displays the cookies.
|
28
|
+
def cookies_recipe
|
29
|
+
return Proc.new {|b| ['Cookies', b[:cookies].awesome_print_html, {:id => 'cookies'}]}
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/lib/debug-bar.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
<div class="callback-box dbar-togglable" style="border:1px solid gray; margin:4px; padding:4px;">
|
2
|
+
<% if opts[:id] %>
|
3
|
+
<a href="" id="<%= opts[:id] %>" class='dbar-toggle persistent'>
|
4
|
+
<% else %>
|
5
|
+
<a href="" class='dbar-toggle'>
|
6
|
+
<% end %>
|
7
|
+
[x] <span class="title" style="font-weight:bold; color: #000000;"><%= title %></span>
|
8
|
+
</a>
|
9
|
+
<div class="dbar-content <%= 'show' unless opts[:hidden] %>" <%= 'style="display:none"' if opts[:hidden] %> >
|
10
|
+
<%= content %>
|
11
|
+
</div>
|
12
|
+
</div>
|
@@ -0,0 +1,142 @@
|
|
1
|
+
<style>
|
2
|
+
.dbar-content pre {
|
3
|
+
background: #a9a9a9;
|
4
|
+
margin: 0;
|
5
|
+
padding: 0;
|
6
|
+
font-size: 10px;
|
7
|
+
font-family: "Lucida Console", Monaco, monospace;
|
8
|
+
}
|
9
|
+
|
10
|
+
.dbar-content pre pre {
|
11
|
+
background: #a9a9a9;
|
12
|
+
margin: 0;
|
13
|
+
padding: 0;
|
14
|
+
border: 0;
|
15
|
+
font-size: 10px;
|
16
|
+
font-family: "Lucida Console", Monaco, monospace;
|
17
|
+
display: inline;
|
18
|
+
}
|
19
|
+
|
20
|
+
</style>
|
21
|
+
|
22
|
+
<div id="debug-bar" style="text-align:left; border:1px solid yellow; background:#ffffaa; width:24px; position:fixed; top:0; left:0; z-index:999">
|
23
|
+
<a href="" id="debug-toggle">[x]</a>
|
24
|
+
<div id="debug-data" style="font-size:9pt; display:none; overflow:auto;">
|
25
|
+
<%= content %>
|
26
|
+
</div>
|
27
|
+
</div>
|
28
|
+
|
29
|
+
<script>
|
30
|
+
/*!
|
31
|
+
* jQuery Cookie Plugin
|
32
|
+
* https://github.com/carhartl/jquery-cookie
|
33
|
+
*
|
34
|
+
* Copyright 2011, Klaus Hartl
|
35
|
+
* Dual licensed under the MIT or GPL Version 2 licenses.
|
36
|
+
* http://www.opensource.org/licenses/mit-license.php
|
37
|
+
* http://www.opensource.org/licenses/GPL-2.0
|
38
|
+
*/
|
39
|
+
(function($) {
|
40
|
+
$.cookie = function(key, value, options) {
|
41
|
+
|
42
|
+
// key and at least value given, set cookie...
|
43
|
+
if (arguments.length > 1 && (!/Object/.test(Object.prototype.toString.call(value)) || value === null || value === undefined)) {
|
44
|
+
options = $.extend({}, options);
|
45
|
+
|
46
|
+
if (value === null || value === undefined) {
|
47
|
+
options.expires = -1;
|
48
|
+
}
|
49
|
+
|
50
|
+
if (typeof options.expires === 'number') {
|
51
|
+
var days = options.expires, t = options.expires = new Date();
|
52
|
+
t.setDate(t.getDate() + days);
|
53
|
+
}
|
54
|
+
|
55
|
+
value = String(value);
|
56
|
+
|
57
|
+
return (document.cookie = [
|
58
|
+
encodeURIComponent(key), '=', options.raw ? value : encodeURIComponent(value),
|
59
|
+
options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE
|
60
|
+
options.path ? '; path=' + options.path : '',
|
61
|
+
options.domain ? '; domain=' + options.domain : '',
|
62
|
+
options.secure ? '; secure' : ''
|
63
|
+
].join(''));
|
64
|
+
}
|
65
|
+
|
66
|
+
// key and possibly options given, get cookie...
|
67
|
+
options = value || {};
|
68
|
+
var decode = options.raw ? function(s) { return s; } : decodeURIComponent;
|
69
|
+
|
70
|
+
var pairs = document.cookie.split('; ');
|
71
|
+
for (var i = 0, pair; pair = pairs[i] && pairs[i].split('='); i++) {
|
72
|
+
if (decode(pair[0]) === key) return decode(pair[1] || ''); // IE saves cookies with empty string as "c; ", e.g. without "=" as opposed to EOMB, thus pair[1] may be undefined
|
73
|
+
}
|
74
|
+
return null;
|
75
|
+
};
|
76
|
+
})(jQuery);
|
77
|
+
</script>
|
78
|
+
<script>
|
79
|
+
jQuery('document').ready(function(){
|
80
|
+
var toggleDebugBar = function(){
|
81
|
+
var data = jQuery('#debug-data').toggle();
|
82
|
+
jQuery('#debug-bar').css('width', data.is(':visible') ? 'auto' : '24px');
|
83
|
+
jQuery('#debug-bar').css('max-height', data.is(':visible') ? '100%' : '24px');
|
84
|
+
jQuery('#debug-bar').css('overflow-y', data.is(':visible') ? 'auto' : 'hidden');
|
85
|
+
};
|
86
|
+
|
87
|
+
jQuery('#debug-toggle').on('click', function(e){
|
88
|
+
toggleDebugBar();
|
89
|
+
return false;
|
90
|
+
});
|
91
|
+
|
92
|
+
jQuery('body').bind('keydown', function(e){
|
93
|
+
if(e.keyCode == 192 && e.ctrlKey == true)
|
94
|
+
toggleDebugBar();
|
95
|
+
});
|
96
|
+
|
97
|
+
// Debug-bar section specific toggle (may differ from generic toggle.
|
98
|
+
// This is meant to be consumable by callback makers.
|
99
|
+
jQuery('.dbar-toggle').bind('click', function(){
|
100
|
+
var toggle = jQuery(this);
|
101
|
+
var content = toggle.closest('.dbar-togglable').find('.dbar-content');
|
102
|
+
content.first().toggle();
|
103
|
+
return false;
|
104
|
+
});
|
105
|
+
|
106
|
+
// Generic togglable inside of debug bar callback content area.
|
107
|
+
jQuery('.dbar-content').find('.toggle-switch').click(function(){
|
108
|
+
var toggle_content = jQuery(this).siblings('.toggle-content');
|
109
|
+
toggle_content.toggle();
|
110
|
+
return false;
|
111
|
+
});
|
112
|
+
|
113
|
+
jQuery('.callback-box .dbar-toggle.persistent').bind('click', function() {
|
114
|
+
// caching
|
115
|
+
var toggle = jQuery(this);
|
116
|
+
var id = toggle.attr('id');
|
117
|
+
|
118
|
+
// Deal with an empty cookie and split the commas
|
119
|
+
var debug_bar_array = jQuery.cookie('debug_bar') ? jQuery.cookie('debug_bar').split(',') : [];
|
120
|
+
|
121
|
+
// Just a double check it has an id
|
122
|
+
if (id)
|
123
|
+
{
|
124
|
+
// Decide which way to toggle this key
|
125
|
+
var id_index = debug_bar_array.indexOf(id);
|
126
|
+
if (id_index > -1)
|
127
|
+
{
|
128
|
+
// Remove id from cookie
|
129
|
+
debug_bar_array.splice(id_index, 1);
|
130
|
+
}
|
131
|
+
else
|
132
|
+
{
|
133
|
+
// Add the id into the list to keep open
|
134
|
+
debug_bar_array.push(id);
|
135
|
+
}
|
136
|
+
|
137
|
+
// Rejoin the cookie keys and store
|
138
|
+
jQuery.cookie('debug_bar', debug_bar_array.join(','), { path: '/', expires: 7 });
|
139
|
+
}
|
140
|
+
});
|
141
|
+
});
|
142
|
+
</script>
|
data/spec/base_spec.rb
ADDED
@@ -0,0 +1,363 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
|
4
|
+
class TestDefaultRecipeBook < DebugBar::RecipeBook::Base
|
5
|
+
|
6
|
+
def params_recipe(opts={})
|
7
|
+
return Proc.new do |b|
|
8
|
+
params_s = b[:params].awesome_print_html
|
9
|
+
hidden = params_s.length > opts.fetch(:cutoff, 512)
|
10
|
+
puts [params_s.length, hidden, opts].inspect
|
11
|
+
['Params', params_s, {:hidden => hidden, :id => 'params'}]
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def block_test_recipe(arg=nil)
|
16
|
+
return Proc.new do |b|
|
17
|
+
['Block Test', "|arg|#{arg}|arg| |block|#{yield(b) if block_given?}|block|", {}]
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
describe DebugBar::Base do
|
24
|
+
|
25
|
+
describe 'callbacks' do
|
26
|
+
|
27
|
+
before(:each) do
|
28
|
+
@debug_bar = DebugBar::Base.new
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'should register recipes in initializer after the block is called' do
|
32
|
+
debug_bar = DebugBar::Base.new(:params) do |bar|
|
33
|
+
bar.add_recipe_book(TestDefaultRecipeBook)
|
34
|
+
bar.callbacks.length.should == 0
|
35
|
+
end
|
36
|
+
|
37
|
+
debug_bar.callbacks.length.should == 1
|
38
|
+
end
|
39
|
+
|
40
|
+
|
41
|
+
it 'should register blocks' do
|
42
|
+
@debug_bar.add {|b| ["Test Block", "Test Content", {}]}
|
43
|
+
|
44
|
+
@debug_bar.callbacks.length.should == 1
|
45
|
+
@debug_bar.callbacks.first.should be_kind_of(Proc)
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'should register recipes' do
|
49
|
+
@debug_bar.add_recipe_book(TestDefaultRecipeBook)
|
50
|
+
@debug_bar.add(:params)
|
51
|
+
|
52
|
+
@debug_bar.callbacks.length.should == 1
|
53
|
+
@debug_bar.callbacks.first.should be_kind_of(Proc)
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'should error on nil' do
|
57
|
+
lambda {@debug_bar.add(nil)}.should raise_error(ArgumentError)
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
describe 'render' do
|
63
|
+
|
64
|
+
before(:each) do
|
65
|
+
@debug_bar = DebugBar::Base.new
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'should render blocks' do
|
69
|
+
title = "Testing The Block"
|
70
|
+
content = "<pre>Alpha Beta</pre>"
|
71
|
+
@debug_bar.add {|b| [title, content, {}]}
|
72
|
+
|
73
|
+
html = @debug_bar.render(binding)
|
74
|
+
|
75
|
+
html.should be_kind_of(String)
|
76
|
+
html.should_not be_empty
|
77
|
+
|
78
|
+
html.index(title).should_not be_nil
|
79
|
+
html.index(content).should_not be_nil
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'should render as html_safe string' do
|
83
|
+
@debug_bar.add {|b| ["foo", "bar", {}]}
|
84
|
+
html = @debug_bar.render(binding)
|
85
|
+
html.should be_kind_of(ActiveSupport::SafeBuffer)
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'should render with a given binding' do
|
89
|
+
@debug_bar.add {|b| ["Title", "||#{b.eval('foobar')}||", {}]}
|
90
|
+
|
91
|
+
foobar = "Spoilers!"
|
92
|
+
html = @debug_bar.render(binding)
|
93
|
+
|
94
|
+
html.should be_kind_of(String)
|
95
|
+
|
96
|
+
html.index(foobar).should_not be_nil
|
97
|
+
end
|
98
|
+
|
99
|
+
it 'should render with a custom decorated binding' do
|
100
|
+
@debug_bar.add {|b| ["Binding", "||get is #{b.respond_to?(:[])}||", {}]}
|
101
|
+
|
102
|
+
binding.should_not respond_to(:[])
|
103
|
+
html = @debug_bar.render(binding)
|
104
|
+
|
105
|
+
html.index("||get is true||").should_not be_nil
|
106
|
+
end
|
107
|
+
|
108
|
+
it 'should render recipes' do
|
109
|
+
@debug_bar.add_recipe_book(TestDefaultRecipeBook)
|
110
|
+
@debug_bar.add(:params)
|
111
|
+
params = {:given_name => 'Amelia', :family_name => 'Pond'}
|
112
|
+
|
113
|
+
html = @debug_bar.render(binding)
|
114
|
+
|
115
|
+
html.should be_kind_of(String)
|
116
|
+
html.should_not be_empty
|
117
|
+
|
118
|
+
html.index('Amelia').should_not be_nil
|
119
|
+
html.index(':given_name').should_not be_nil
|
120
|
+
html.index('dbar-content show').should_not be_nil # See if params was expanded.
|
121
|
+
end
|
122
|
+
|
123
|
+
it 'should render recipes with args' do
|
124
|
+
@debug_bar.add_recipe_book(TestDefaultRecipeBook)
|
125
|
+
@debug_bar.add(:params, :cutoff => 12)
|
126
|
+
|
127
|
+
params = {:given_name => 'Amelia', :family_name => 'Pond'}
|
128
|
+
|
129
|
+
html = @debug_bar.render(binding)
|
130
|
+
|
131
|
+
html.should be_kind_of(String)
|
132
|
+
html.should_not be_empty
|
133
|
+
|
134
|
+
html.index('Amelia').should_not be_nil
|
135
|
+
html.index(':given_name').should_not be_nil
|
136
|
+
html.index('dbar-content show').should be_nil # See if params was collapsed due to cutoff.
|
137
|
+
end
|
138
|
+
|
139
|
+
it 'should render recipes with block args' do
|
140
|
+
@debug_bar.add_recipe_book(TestDefaultRecipeBook)
|
141
|
+
@debug_bar.add(:block_test) {|b| b.class.name}
|
142
|
+
|
143
|
+
html = @debug_bar.render(binding)
|
144
|
+
|
145
|
+
html.should be_kind_of(String)
|
146
|
+
html.should_not be_empty
|
147
|
+
|
148
|
+
html.index('|block|Binding|block|').should_not be_nil
|
149
|
+
end
|
150
|
+
|
151
|
+
it 'should render the callback_box' do
|
152
|
+
@debug_bar.add {|b| ["foo", "bar", {}]}
|
153
|
+
html = @debug_bar.render(binding)
|
154
|
+
|
155
|
+
html.index('callback-box').should_not be_nil # Picked the CSS class as a good indicator of presence.
|
156
|
+
end
|
157
|
+
|
158
|
+
it 'should render the layout' do
|
159
|
+
@debug_bar.add {|b| ["foo", "bar", {}]}
|
160
|
+
html = @debug_bar.render(binding)
|
161
|
+
|
162
|
+
html.index('debug-bar').should_not be_nil # Picked the presence of CSS class as a good inidcator of presence.
|
163
|
+
end
|
164
|
+
|
165
|
+
it 'should render on crash in callback' do
|
166
|
+
@debug_bar.add {|b| raise RuntimeError, "Uh-oh, you didn't handle the exception!"}
|
167
|
+
html = ''
|
168
|
+
lambda {html = @debug_bar.render(binding)}.should_not raise_error
|
169
|
+
html.index('ERROR').should_not be_nil
|
170
|
+
end
|
171
|
+
|
172
|
+
end
|
173
|
+
|
174
|
+
describe 'options' do
|
175
|
+
|
176
|
+
before(:each) do
|
177
|
+
@debug_bar = DebugBar::Base.new
|
178
|
+
end
|
179
|
+
|
180
|
+
it 'should interpret missing opts from callback as {}' do
|
181
|
+
@debug_bar.add {|b| ['foo', 'bar']}
|
182
|
+
|
183
|
+
html = @debug_bar.render(binding)
|
184
|
+
|
185
|
+
html.should be_kind_of(String)
|
186
|
+
html.should_not be_empty
|
187
|
+
|
188
|
+
html.index('foo').should_not be_nil
|
189
|
+
html.index('bar').should_not be_nil
|
190
|
+
end
|
191
|
+
|
192
|
+
it 'should interpret nil opts from callback as {}' do
|
193
|
+
@debug_bar.add {|b| ['foo', 'bar', nil]}
|
194
|
+
|
195
|
+
html = @debug_bar.render(binding)
|
196
|
+
|
197
|
+
html.should be_kind_of(String)
|
198
|
+
html.should_not be_empty
|
199
|
+
|
200
|
+
html.index('foo').should_not be_nil
|
201
|
+
html.index('bar').should_not be_nil
|
202
|
+
end
|
203
|
+
|
204
|
+
it 'should default :hidden option to true if not explicitly given and session remembers it as open based on id' do
|
205
|
+
# Emulate saving of open callback box ids in cookies.
|
206
|
+
cookies = {:debug_bar => 'rory_williams,amelia_pond'}
|
207
|
+
# Put into binding.
|
208
|
+
b = binding
|
209
|
+
|
210
|
+
html_rory = DebugBar::Base.new.add {|b| ['foo', 'bar', :id => 'rory_williams']}.render(b)
|
211
|
+
html_rose = DebugBar::Base.new.add {|b| ['foo', 'bar', :id => 'rose_tyler']}.render(b)
|
212
|
+
|
213
|
+
# Extract the classes from the first div that has a dbar-content class in it.
|
214
|
+
extract_classes = lambda do |html|
|
215
|
+
m = /<div[^>]+class=['"]([^>'"]*dbar-content[^>'"]*)[^>]+>/.match(html)
|
216
|
+
return m.to_a[1].to_s.split(/\s+/)
|
217
|
+
end
|
218
|
+
|
219
|
+
html_rory.should include('show')
|
220
|
+
html_rose.should_not include('show')
|
221
|
+
end
|
222
|
+
|
223
|
+
it 'should recognize :hidden options' do
|
224
|
+
html_hidden = DebugBar::Base.new.add {|b| ['foo', 'bar', :hidden => true]}.render(binding)
|
225
|
+
html_nohide = DebugBar::Base.new.add {|b| ['foo', 'bar', :hidden => false]}.render(binding)
|
226
|
+
|
227
|
+
# Extract the classes from the first div that has a dbar-content class in it.
|
228
|
+
extract_classes = lambda do |html|
|
229
|
+
m = /<div[^>]+class=['"]([^>'"]*dbar-content[^>'"]*)[^>]+>/.match(html)
|
230
|
+
return m.to_a[1].to_s.split(/\s+/)
|
231
|
+
end
|
232
|
+
|
233
|
+
extract_classes.call(html_hidden).should_not include('show')
|
234
|
+
extract_classes.call(html_nohide).should include('show')
|
235
|
+
end
|
236
|
+
|
237
|
+
end
|
238
|
+
|
239
|
+
describe 'recipe books' do
|
240
|
+
|
241
|
+
class TestBook < DebugBar::RecipeBook::Base
|
242
|
+
def duplicated_recipe
|
243
|
+
Proc.new {|b| :test_book_duplicated_recipe}
|
244
|
+
end
|
245
|
+
|
246
|
+
def beta_recipe
|
247
|
+
Proc.new {|b| :test_book_beta_recipe}
|
248
|
+
end
|
249
|
+
|
250
|
+
def some_helper
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
class AnotherTestBook < DebugBar::RecipeBook::Base
|
255
|
+
def duplicated_recipe
|
256
|
+
Proc.new {|b| :another_test_book_duplicated_recipe}
|
257
|
+
end
|
258
|
+
|
259
|
+
def gamma_recipe
|
260
|
+
Proc.new {|b| :another_test_book_gamma_recipe}
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
before(:each) do
|
265
|
+
@book_class = TestBook # TODO: This is a poor choice, we should create one just for testing.
|
266
|
+
@book = @book_class.new
|
267
|
+
|
268
|
+
@base_debug_bar = DebugBar::Base.new
|
269
|
+
@debug_bar = DebugBar::Base.new {|bar| bar.add_recipe_book(TestBook); bar.add_recipe_book(AnotherTestBook)}
|
270
|
+
end
|
271
|
+
|
272
|
+
it 'should add recipe books by class' do
|
273
|
+
@base_debug_bar.add_recipe_book(@book_class)
|
274
|
+
|
275
|
+
@base_debug_bar.recipe_books.length.should == 1
|
276
|
+
@base_debug_bar.recipe_books.first.should be_kind_of(@book_class)
|
277
|
+
end
|
278
|
+
|
279
|
+
it 'should add recipe books by instance' do
|
280
|
+
@base_debug_bar.add_recipe_book(@book)
|
281
|
+
|
282
|
+
@base_debug_bar.recipe_books.length.should == 1
|
283
|
+
@base_debug_bar.recipe_books.first.should be_kind_of(@book_class)
|
284
|
+
end
|
285
|
+
|
286
|
+
it 'should return the list of known recipes' do
|
287
|
+
@debug_bar.recipes.sort.should == [:beta, :duplicated, :gamma].sort
|
288
|
+
end
|
289
|
+
|
290
|
+
it 'should return a recipe callback if one is found' do
|
291
|
+
callback = @debug_bar.recipe_callback(:beta)
|
292
|
+
callback.should be_kind_of(Proc)
|
293
|
+
callback.call.should == :test_book_beta_recipe
|
294
|
+
end
|
295
|
+
|
296
|
+
it 'should raise an Argument Error if a recipe is not found' do
|
297
|
+
lambda {@debug_bar.recipe_callback(:zeta)}.should raise_error(ArgumentError)
|
298
|
+
end
|
299
|
+
|
300
|
+
it 'should use the last found instance of a recipe when it is duplicated' do
|
301
|
+
callback = @debug_bar.recipe_callback(:duplicated)
|
302
|
+
callback.should be_kind_of(Proc)
|
303
|
+
callback.call.should == :another_test_book_duplicated_recipe
|
304
|
+
end
|
305
|
+
|
306
|
+
end
|
307
|
+
|
308
|
+
describe 'subclass overrides' do
|
309
|
+
|
310
|
+
class SubclassTestBook < DebugBar::RecipeBook::Base
|
311
|
+
|
312
|
+
def beta_recipe
|
313
|
+
Proc.new {|b| :test_book_beta_recipe}
|
314
|
+
end
|
315
|
+
|
316
|
+
end
|
317
|
+
|
318
|
+
class SubclassInstanceTestBook < DebugBar::RecipeBook::Base
|
319
|
+
|
320
|
+
def foo_recipe
|
321
|
+
Proc.new {|b| :instance_test_book_foo_recipe}
|
322
|
+
end
|
323
|
+
|
324
|
+
end
|
325
|
+
|
326
|
+
class SubclassTestBar < DebugBar::Base
|
327
|
+
|
328
|
+
private
|
329
|
+
|
330
|
+
def default_recipe_books
|
331
|
+
return [SubclassTestBook, SubclassInstanceTestBook.new]
|
332
|
+
end
|
333
|
+
|
334
|
+
def default_recipes
|
335
|
+
return [:beta]
|
336
|
+
end
|
337
|
+
|
338
|
+
def template_search_paths
|
339
|
+
return :standin_for_an_array_of_pathnames
|
340
|
+
end
|
341
|
+
|
342
|
+
end
|
343
|
+
|
344
|
+
before(:each) do
|
345
|
+
@debug_bar = SubclassTestBar.new
|
346
|
+
end
|
347
|
+
|
348
|
+
it 'should add recipe books from the default recipe books method' do
|
349
|
+
@debug_bar.recipe_books.length.should == 2
|
350
|
+
@debug_bar.recipe_books.each {|book| book.should be_kind_of(DebugBar::RecipeBook::Base)}
|
351
|
+
end
|
352
|
+
|
353
|
+
it 'should add recipes from the default recipe method' do
|
354
|
+
@debug_bar.recipes.sort.should == [:beta, :foo].sort
|
355
|
+
end
|
356
|
+
|
357
|
+
it 'should allow overriding of the template path' do
|
358
|
+
@debug_bar.send(:template_search_paths).should == :standin_for_an_array_of_pathnames
|
359
|
+
end
|
360
|
+
|
361
|
+
end
|
362
|
+
|
363
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class TestContext
|
4
|
+
TARDIS = :tardis
|
5
|
+
@@gallifrey = :gallifrey
|
6
|
+
$the_doctor = :the_doctor
|
7
|
+
|
8
|
+
def get_binding
|
9
|
+
amelia_pond = :amelia_pond
|
10
|
+
@river_song = :river_song
|
11
|
+
|
12
|
+
return binding
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe DebugBar::Ext::Binding do
|
17
|
+
|
18
|
+
describe '[] method' do
|
19
|
+
|
20
|
+
before(:all) do
|
21
|
+
@binding = TestContext.new.get_binding
|
22
|
+
@binding.extend(DebugBar::Ext::Binding)
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'should exist on binding' do
|
26
|
+
@binding.should respond_to(:[])
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'should get local variables' do
|
30
|
+
@binding[:amelia_pond].should == :amelia_pond
|
31
|
+
@binding['amelia_pond'].should == :amelia_pond
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'should get instance varaibles' do
|
35
|
+
@binding[:@river_song].should == :river_song
|
36
|
+
@binding['@river_song'].should == :river_song
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'should get global variables' do
|
40
|
+
@binding[:$the_doctor].should == :the_doctor
|
41
|
+
@binding['$the_doctor'].should == :the_doctor
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'should get constants' do
|
45
|
+
@binding[:TARDIS].should == :tardis
|
46
|
+
@binding['TARDIS'].should == :tardis
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'should get class variables' do
|
50
|
+
@binding[:@@gallifrey].should == :gallifrey
|
51
|
+
@binding['@@gallifrey'].should == :gallifrey
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'should not perform arbirary code' do
|
55
|
+
lambda {@binding['1+1']}.should raise_error(NameError)
|
56
|
+
lambda {@binding['amelia_pond.class']}.should raise_error(NameError)
|
57
|
+
lambda {@binding['def foo']}.should raise_error(NameError)
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|