debug-bar 1.0.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.
- 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
data/.gitignore
ADDED
data/.rbenv-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
ruby-1.9.2
|
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color --format documentation spec
|
data/Gemfile
ADDED
data/README.rdoc
ADDED
@@ -0,0 +1,192 @@
|
|
1
|
+
= Overview
|
2
|
+
|
3
|
+
DebugBar offers an easy way to show developer/debugging information on a a web
|
4
|
+
page in an unobtrustive way. It features keyboard shortcuts for showing/hiding
|
5
|
+
the bar (Ctrl-~), and memory (through cookies) of which information to show.
|
6
|
+
|
7
|
+
DebugBar uses a modular architecture utilizing callback lambdas to produce
|
8
|
+
the contents of the DebugBar.
|
9
|
+
|
10
|
+
DebugBar::Base is the base debug bar, suitable for use in any application, and
|
11
|
+
comes without any pre-added callbacks.
|
12
|
+
|
13
|
+
DebugBar::Default is a default debug bar suitible for a typical Rails application,
|
14
|
+
and includes several default callbacks meaningful in such an environment.
|
15
|
+
|
16
|
+
= Installation
|
17
|
+
|
18
|
+
To use DebugBar, your application must include jQuery in rendered pages.
|
19
|
+
|
20
|
+
Typically, Rails applications create a debug_bar initializer that defines a
|
21
|
+
DEBUG_BAR constant which refers to the configured DebugBar instance. Customization
|
22
|
+
and setup are typically done in this file.
|
23
|
+
|
24
|
+
= Examples
|
25
|
+
|
26
|
+
== Basic Usage
|
27
|
+
|
28
|
+
Typically the debug bar is added to your layout template with the following code
|
29
|
+
DebugBar::Default.new.render(binding)
|
30
|
+
though it is typically better to pre-instantiate your debug bar instance as a
|
31
|
+
constant in an initializer, and then reference it in your layout, like so:
|
32
|
+
|
33
|
+
# In your initializer
|
34
|
+
DEBUG_BAR = DebugBar::Default.new do |debug_bar|
|
35
|
+
# Do additional setup, such as registering custom recipe books and callbacks here.
|
36
|
+
end
|
37
|
+
|
38
|
+
# In your layout view template
|
39
|
+
DEBUG_BAR.render(binding)
|
40
|
+
|
41
|
+
Additionally, it is common to include code that controls the optional rendering
|
42
|
+
of the debug bar based on environement and/or parameters; for example
|
43
|
+
DEBUG_BAR.render(binding) if Rails.env=='development' || params.include?(:debugger)
|
44
|
+
could be used in Rails.
|
45
|
+
|
46
|
+
== Custom Callbacks
|
47
|
+
|
48
|
+
While there are a basic set of callbacks available, the real power of DebugBar
|
49
|
+
is the ability to add custom callbacks.
|
50
|
+
|
51
|
+
In the context of a Rails application, custom callbacks are typically added
|
52
|
+
to the config/initializer where the debug_bar is instantiated.
|
53
|
+
|
54
|
+
=== Basic
|
55
|
+
|
56
|
+
Custom callbacks are typically Proc objects that take an evaluation binding
|
57
|
+
context as an argument, and produce an array of the form of two to three elements:
|
58
|
+
|
59
|
+
[title] The display title for this callback box.
|
60
|
+
[body] The raw HTML string to render.
|
61
|
+
[opts] A hash of options to pass to the renderer, usually used to control box layout options. This is optional.
|
62
|
+
|
63
|
+
Thus, if one wanted a debug box to display the time, one might do
|
64
|
+
debug_bar.add {|binding| ['Time', Time.now.to_s]}
|
65
|
+
|
66
|
+
A more complex example would to output the params hash. Note that since the output
|
67
|
+
is raw HTML, we must replace all instances of '<' with '<'. (A proper implementation
|
68
|
+
would escape all escapable entities.)
|
69
|
+
debug_bar.add do |binding|
|
70
|
+
body = binding.eval('params').inspect.gsub('<','<')
|
71
|
+
['Params', body]
|
72
|
+
end
|
73
|
+
|
74
|
+
Note that we using binding.eval to extract variable names from the binding by
|
75
|
+
executing snippets of code. This raises two points:
|
76
|
+
* Any code can be evaluated in the binding in this
|
77
|
+
manor, thus choice of render binding has a major impact on the information
|
78
|
+
available for display.
|
79
|
+
* As a convenience, variables can be extracted from the binding with the <code>[]</code> method,
|
80
|
+
thus
|
81
|
+
body = binding[:params].inspect
|
82
|
+
could be substituted for
|
83
|
+
body = binding.eval('params').inspect
|
84
|
+
|
85
|
+
|
86
|
+
=== Rails Render
|
87
|
+
|
88
|
+
If rendering the DebugBar from within a Rails template (e.g. the application
|
89
|
+
layout), you can use Rails render commands in the callback via a binding.eval
|
90
|
+
to render any template and output the results to the debug bar. This is
|
91
|
+
convenient way to render any input, though the use of custom recipe books
|
92
|
+
should be considered if you do this often.
|
93
|
+
|
94
|
+
=== Options
|
95
|
+
|
96
|
+
Callbacks may provide the following options:
|
97
|
+
|
98
|
+
[:id] The HTML id for the callback for use with custom javascript hooks, and remembered settings.
|
99
|
+
[:hidden] Controls default state of disclosure arrow for the callback's content.
|
100
|
+
Note that if an :id is provided, the state can be remembered between requests.
|
101
|
+
|
102
|
+
=== UI Tools
|
103
|
+
|
104
|
+
Note that debug-bar callbacks can use the 'toggle-switch' and 'toggle-content'
|
105
|
+
classes to drive toggleable hide/show behavior inside of the debug-bar. To
|
106
|
+
do so, add the 'toggle-switch' class to the link that causes toggle, and the
|
107
|
+
'toggle-content' class to the <i>sibling</i> element that will toggle.
|
108
|
+
|
109
|
+
For example:
|
110
|
+
|
111
|
+
<div>
|
112
|
+
<a href="" class="toggle-switch">Details</a>
|
113
|
+
<div class="toggle-content" style="display:none">
|
114
|
+
Lot's of content here.
|
115
|
+
</div>
|
116
|
+
</div>
|
117
|
+
|
118
|
+
This would present a "Details" link that would reveal the content div.
|
119
|
+
|
120
|
+
== Custom Recipes
|
121
|
+
|
122
|
+
While it is convenient to define one-off callbacks directly via add, it is
|
123
|
+
often both cleaner and more useful to create re-usable callback recipes in
|
124
|
+
RecipeBooks. Additionally, RecipeBooks provide some convenience methods not
|
125
|
+
available in your own initializers.
|
126
|
+
|
127
|
+
Essentially, subclasses of RecipeBook::Base are factory classes that contain
|
128
|
+
instance methods that generate Proc objects that are used as callbacks. What
|
129
|
+
makes RecipeBook special over any random factory class is the convenience
|
130
|
+
methods it gives and the tight integration with DebugBar::Base instances.
|
131
|
+
|
132
|
+
=== Setup
|
133
|
+
|
134
|
+
One can manually add a RecipeBook by class or instance using the +add_recipe_book+
|
135
|
+
method. For example
|
136
|
+
debug_bar.add_recipe_book(DebugBar::RecipeBook::Default)
|
137
|
+
or
|
138
|
+
book = DebugBar::RecipeBook::Default.new
|
139
|
+
debug_bar.add_recipe_book(book)
|
140
|
+
|
141
|
+
=== Creating Your Own
|
142
|
+
|
143
|
+
To create your own recipe book, simply subclass another recipe book and add
|
144
|
+
recipe generation inatance methods that follow a few simple rules:
|
145
|
+
1. They must be the recipe name suffixed with `_recipe'.
|
146
|
+
2. They must be able to support being called with no elements to support
|
147
|
+
short-hand addition of a recipe. (This does no preclude supporting optional
|
148
|
+
arguments and manual addition of the generated block.
|
149
|
+
|
150
|
+
Recipes in a RecipeBook::Base subclass get access to special functionality, such
|
151
|
+
as being able to use templates at class-defined locations.
|
152
|
+
|
153
|
+
= Contact
|
154
|
+
|
155
|
+
Jeff Reinecke <jreinecke@whitepages.com>
|
156
|
+
Keith Stone <kstone@whitepages.com>
|
157
|
+
|
158
|
+
= Feature Roadmap
|
159
|
+
|
160
|
+
* No future features expected at this time.
|
161
|
+
|
162
|
+
= Version History
|
163
|
+
|
164
|
+
[1.0.0 - 2012-Jul-13] Initial Release.
|
165
|
+
|
166
|
+
= License
|
167
|
+
|
168
|
+
Copyright (c) 2012, WhitePages, Inc.
|
169
|
+
All rights reserved.
|
170
|
+
|
171
|
+
Redistribution and use in source and binary forms, with or without
|
172
|
+
modification, are permitted provided that the following conditions are met:
|
173
|
+
* Redistributions of source code must retain the above copyright
|
174
|
+
notice, this list of conditions and the following disclaimer.
|
175
|
+
* Redistributions in binary form must reproduce the above copyright
|
176
|
+
notice, this list of conditions and the following disclaimer in the
|
177
|
+
documentation and/or other materials provided with the distribution.
|
178
|
+
* Neither the name of the company nor the
|
179
|
+
names of its contributors may be used to endorse or promote products
|
180
|
+
derived from this software without specific prior written permission.
|
181
|
+
|
182
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
183
|
+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
184
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
185
|
+
DISCLAIMED. IN NO EVENT SHALL WHITEPAGES, INC. BE LIABLE FOR ANY
|
186
|
+
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
187
|
+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
188
|
+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
189
|
+
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
190
|
+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
191
|
+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
192
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
#!/usr/bin/env rake
|
2
|
+
# Add your own tasks in files placed in lib/tasks ending in .rake,
|
3
|
+
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
|
4
|
+
require 'pathname'
|
5
|
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../Gemfile", Pathname.new(__FILE__).realpath)
|
6
|
+
require 'rubygems'
|
7
|
+
require 'bundler/setup'
|
8
|
+
require 'gemtools/rake_task'
|
9
|
+
|
10
|
+
require 'rake/dsl_definition'
|
11
|
+
require "rspec/core/rake_task"
|
12
|
+
|
13
|
+
RSpec::Core::RakeTask.new(:spec) do |spec|
|
14
|
+
spec.rspec_opts = ['--backtrace']
|
15
|
+
end
|
16
|
+
|
17
|
+
Bundler::GemHelper.install_tasks
|
18
|
+
Gemtools::RakeTask.install_tasks
|
19
|
+
|
20
|
+
task :default => :spec
|
21
|
+
|
22
|
+
require 'rake/rdoctask'
|
23
|
+
|
24
|
+
Rake::RDocTask.new do |rdoc|
|
25
|
+
rdoc.rdoc_dir = "rdoc"
|
26
|
+
rdoc.rdoc_files.add "lib/**/*.rb", "README.rdoc"
|
27
|
+
end
|
data/debug-bar.gemspec
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path('../lib', __FILE__)
|
3
|
+
require 'debug-bar/version'
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = 'debug-bar'
|
7
|
+
s.version = DebugBar::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ['Jeff Reinecke', 'Keith Stone']
|
10
|
+
s.email = ['jreinecke@whitepages.com', 'kstone@whitepages.com']
|
11
|
+
s.homepage = 'https://github.com/whitepages/debug-bar'
|
12
|
+
s.summary = 'Debug Bar'
|
13
|
+
s.description = 'Base generic debug bar implementation.'
|
14
|
+
s.licenses = ['BSD']
|
15
|
+
|
16
|
+
s.add_dependency 'erubis'
|
17
|
+
s.add_dependency 'activesupport'
|
18
|
+
s.add_dependency 'awesome_print'
|
19
|
+
|
20
|
+
s.files = `git ls-files`.split("\n")
|
21
|
+
s.test_files = `git ls-files -- {test,spec}/*`.split("\n")
|
22
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
23
|
+
s.require_paths = ["lib"]
|
24
|
+
end
|
@@ -0,0 +1,208 @@
|
|
1
|
+
require 'active_support/all'
|
2
|
+
require 'pathname'
|
3
|
+
require 'erubis'
|
4
|
+
|
5
|
+
|
6
|
+
require_relative 'ext'
|
7
|
+
|
8
|
+
# DebugBar is the module namespace for this gem. For the DebugBar base class,
|
9
|
+
# see DebugBar::Base.
|
10
|
+
module DebugBar
|
11
|
+
# = Overview
|
12
|
+
#
|
13
|
+
# DebugBar::Base provides the base methods for all debug bars.
|
14
|
+
#
|
15
|
+
# At it's core, a DebugBar is instantiated with +initialize+, gets callbacks
|
16
|
+
# added with +add_callback+, and then is rendered with +render+.
|
17
|
+
#
|
18
|
+
# Additionally, RecipeBook classes or instance may be added to the DebugBar
|
19
|
+
# via +add_recipe_book+ so that pre-made callbacks may be easily added to the
|
20
|
+
# DebugBar instance via add_callbacks.
|
21
|
+
#
|
22
|
+
# See the README for example usage.
|
23
|
+
#
|
24
|
+
# = Subclassing
|
25
|
+
#
|
26
|
+
# This class is often subclassed to give DebugBars with special behaviors.
|
27
|
+
# If you make a subclass, define <b>private</b> overrides to these methods:
|
28
|
+
# [+default_recipe_books+] Provide a list of recipe books to make available to all instances.
|
29
|
+
# [+default_recipes+] Add a list of recipe callbacks to all instances.
|
30
|
+
# [+template_search_paths+] Override the default formatting template search path.
|
31
|
+
class Base
|
32
|
+
|
33
|
+
# The search path for formatting templates, such as the layout and callback box.
|
34
|
+
# NOTE: This is separate from templates that are used in recipes!
|
35
|
+
TEMPLATE_SEARCH_PATHS = [
|
36
|
+
(Pathname.new(__FILE__).dirname + '../templates')
|
37
|
+
].map {|path| path.expand_path}
|
38
|
+
|
39
|
+
# Initialize a new debug bar. This may optionally take
|
40
|
+
# one or more recipe symbols as arguments.
|
41
|
+
def initialize(*recipes)
|
42
|
+
#Initialize registration variables.
|
43
|
+
@callbacks = []
|
44
|
+
@recipe_books = []
|
45
|
+
# Register defaults.
|
46
|
+
default_recipe_books.each {|book| add_recipe_book(book)}
|
47
|
+
default_recipes.each {|recipe| add_recipe(recipe)}
|
48
|
+
# Give a chance for custom configuration, including addition of books.
|
49
|
+
yield self if block_given?
|
50
|
+
# Now we can add user listed recipes.
|
51
|
+
recipes.each {|recipe| add_recipe(recipe)}
|
52
|
+
end
|
53
|
+
|
54
|
+
# Returns a copy of the raw list of callbacks.
|
55
|
+
attr_reader :callbacks
|
56
|
+
|
57
|
+
# Returns a copy of the list of recipe book instances.
|
58
|
+
attr_reader :recipe_books
|
59
|
+
|
60
|
+
# Adds a recipe book class or instance to the recipe book list for
|
61
|
+
# this debug bar.
|
62
|
+
#
|
63
|
+
# Returns self to support functional programming styles.
|
64
|
+
def add_recipe_book(book)
|
65
|
+
@recipe_books << (book.kind_of?(Class) ? book.new : book)
|
66
|
+
return self
|
67
|
+
end
|
68
|
+
alias_method :add_book, :add_recipe_book
|
69
|
+
|
70
|
+
# Returns the list of recipes recognized by this debug bar.
|
71
|
+
def recipes
|
72
|
+
return @recipe_books.inject([]) {|list,book| list | book.recipes}
|
73
|
+
end
|
74
|
+
|
75
|
+
# Returns the most recently added occurance of the given recipe.
|
76
|
+
def recipe_callback(recipe, *args, &block)
|
77
|
+
book = @recipe_books.reverse.find {|book| book.include?(recipe)}
|
78
|
+
raise ArgumentError, "Could not find recipe #{recipe.inspect}", caller if book.nil?
|
79
|
+
return book.recipe(recipe, *args, &block)
|
80
|
+
end
|
81
|
+
|
82
|
+
# Adds a callback.
|
83
|
+
#
|
84
|
+
# Takes either a recipe (by symbol) or a block.
|
85
|
+
#
|
86
|
+
# The block takes a single argument, the binding of the render context,
|
87
|
+
# and should return either a string, or an array of [title, content, opts].
|
88
|
+
#
|
89
|
+
# Advanced users can call a recipe by name, and provide additional arguments
|
90
|
+
# to configure the recipe further. These arguments are defined by the
|
91
|
+
# recipe factory method, but usually are via an options hash and/or a block.
|
92
|
+
#
|
93
|
+
# Returns self to support functional programming styles.
|
94
|
+
def add_callback(recipe=nil, *args, &callback)
|
95
|
+
callback_proc = recipe.nil? ? callback : recipe_callback(recipe, *args, &callback)
|
96
|
+
raise ArgumentError, "Expected callback to respond to `call': #{callback_proc.inspect}", caller unless callback_proc.respond_to?(:call)
|
97
|
+
@callbacks << callback_proc
|
98
|
+
return self
|
99
|
+
end
|
100
|
+
alias_method :add_recipe, :add_callback
|
101
|
+
alias_method :add, :add_callback
|
102
|
+
|
103
|
+
# Renders the debug bar with the given binding.
|
104
|
+
def render(eval_binding)
|
105
|
+
# Decorate the binding here (NOT in private methods where we don't want automatic behavior)!
|
106
|
+
eval_binding.extend(DebugBar::Ext::Binding)
|
107
|
+
return render_layout(eval_binding)
|
108
|
+
end
|
109
|
+
|
110
|
+
private
|
111
|
+
|
112
|
+
# An initialization callback for adding default recipe books to instances;
|
113
|
+
# this should return an array of recipe book classes or instances.
|
114
|
+
#
|
115
|
+
# On the base class, this returns an empty array; subclasses should override this.
|
116
|
+
def default_recipe_books
|
117
|
+
return []
|
118
|
+
end
|
119
|
+
|
120
|
+
# An initialization callback for adding default recipes to the callbacks
|
121
|
+
# array.
|
122
|
+
#
|
123
|
+
# On the base class, this returns an empty array; subclasses should override this.
|
124
|
+
def default_recipes
|
125
|
+
return []
|
126
|
+
end
|
127
|
+
|
128
|
+
# Returns the template search paths for this instance.
|
129
|
+
#
|
130
|
+
# Paths should be Pathname instances
|
131
|
+
#
|
132
|
+
# Subclasses may override this to change the search path for the formatting
|
133
|
+
# templates such as the layout and callback_box templates.
|
134
|
+
def template_search_paths
|
135
|
+
return TEMPLATE_SEARCH_PATHS
|
136
|
+
end
|
137
|
+
|
138
|
+
# Looks for the given remplate name within the template search paths, and
|
139
|
+
# returns a string containing its contents. The name may be a symbol or string.
|
140
|
+
#
|
141
|
+
# Template names automatically have '.html.erb' appended to them, so call
|
142
|
+
# read_template(:foo)
|
143
|
+
# instead of
|
144
|
+
# read_template('foo.html.erb')
|
145
|
+
def read_template(template)
|
146
|
+
template_name = "#{template}.html.erb"
|
147
|
+
template_path = template_search_paths.map {|base_path| (base_path + template_name).expand_path}.find {|p| p.exist? && p.file?}
|
148
|
+
raise ArgumentError, "Unknown template #{template_name.inspect}. Not in #{template_search_paths.inspect}", caller if template_path.nil?
|
149
|
+
return template_path.read
|
150
|
+
end
|
151
|
+
|
152
|
+
# Renders the callbacks and then renders the layout--all in the given
|
153
|
+
# binding--inserting the callbacks into the layout; returns an html_safe string.
|
154
|
+
def render_layout(eval_binding)
|
155
|
+
content = render_callbacks(@callbacks, eval_binding)
|
156
|
+
return Erubis::Eruby.new(read_template(:layout)).result(:content => content).html_safe
|
157
|
+
end
|
158
|
+
|
159
|
+
# Returns the contactinated set of rendered callbacks usnig the given binding.
|
160
|
+
def render_callbacks(callbacks, eval_binding)
|
161
|
+
return @callbacks.map {|callback| render_callback(callback, eval_binding)}.join("\n")
|
162
|
+
end
|
163
|
+
|
164
|
+
# Renders the given callback in the given binding.
|
165
|
+
def render_callback(callback, eval_binding)
|
166
|
+
# Get the result of the callback
|
167
|
+
obj = begin
|
168
|
+
callback.respond_to?(:call) ? callback.call(eval_binding) : callback
|
169
|
+
rescue Exception => error
|
170
|
+
render_error_callback(error)
|
171
|
+
end
|
172
|
+
|
173
|
+
# Extract the title, content, and opts from the result
|
174
|
+
title, content, opts = case obj
|
175
|
+
when Array
|
176
|
+
obj
|
177
|
+
else
|
178
|
+
['Debug', obj.to_s, {}]
|
179
|
+
end
|
180
|
+
opts ||= {}
|
181
|
+
|
182
|
+
# reverse merge the opts
|
183
|
+
default_hidden = opts[:id].nil? ? false : !cookie_include?(opts[:id], eval_binding)
|
184
|
+
opts = {:hidden => default_hidden}.merge(opts||{})
|
185
|
+
|
186
|
+
# Render the callback in a box
|
187
|
+
return Erubis::Eruby.new( read_template(:callback_box) ).result(:title => title, :content => content, :opts => opts).html_safe
|
188
|
+
end
|
189
|
+
|
190
|
+
def render_error_callback(error, opts={})
|
191
|
+
return [
|
192
|
+
opts.fetch(:title, '**ERROR'),
|
193
|
+
Erubis::Eruby.new(read_template(:error)).result(:error => error).html_safe,
|
194
|
+
{}
|
195
|
+
]
|
196
|
+
end
|
197
|
+
|
198
|
+
# A helper method that--if the eval_binding defines a cookies hash, and
|
199
|
+
# that hash has a :debug_bar key, returns true if it contains the given
|
200
|
+
# id; otherwise it returns false.
|
201
|
+
#
|
202
|
+
# TODO: This code should be refactored to support more use cases as they appear.
|
203
|
+
def cookie_include?(id, eval_binding)
|
204
|
+
debug_bar = eval_binding.eval("defined?(cookies) && cookies[:debug_bar]")
|
205
|
+
debug_bar.nil? ? false : debug_bar.split(',').include?(id)
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module DebugBar
|
2
|
+
# A default DebugBar implementation suitable for use in a Ruby On Rails
|
3
|
+
# application layout template.
|
4
|
+
#
|
5
|
+
# This, of course, may be customized, typically by creating an initializer
|
6
|
+
# file at config/initializers/debug_bar.rb, and populating like so:
|
7
|
+
#
|
8
|
+
# DEBUG_BAR = DebugBar::Default.new do |bar|
|
9
|
+
# bar.add {|b| ['Time', Time.now]}
|
10
|
+
# end
|
11
|
+
class Default < Base
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
# Override superclass method to provide the necessary cookbook.
|
16
|
+
def default_recipe_books
|
17
|
+
return [RecipeBook::Default]
|
18
|
+
end
|
19
|
+
|
20
|
+
# The recipes added to this debug bar by default.
|
21
|
+
def default_recipes
|
22
|
+
return [:params, :session]
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module DebugBar
|
2
|
+
module Ext
|
3
|
+
# Binding extensions that are decorated onto bindings passed into callbacks.
|
4
|
+
module Binding
|
5
|
+
# A regex for matching only valid local, instance, class, and global variables, as well as constatns.
|
6
|
+
VARIABLE_PATTERN = /^(@{1,2}|\$)?([_a-zA-Z]\w*)$/
|
7
|
+
|
8
|
+
# Returns the value of the given variable symbol within the binding.
|
9
|
+
# Supports local, instance, class, or global variables, as well as constants.
|
10
|
+
def [](var)
|
11
|
+
raise NameError, "#{var.inspect} is not a valid variable name" unless var.to_s =~ VARIABLE_PATTERN
|
12
|
+
return self.eval(var.to_s)
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'awesome_print'
|
2
|
+
|
3
|
+
module DebugBar
|
4
|
+
module Ext
|
5
|
+
module Object
|
6
|
+
|
7
|
+
def awesome_print(opts={})
|
8
|
+
return self.ai(opts)
|
9
|
+
end
|
10
|
+
|
11
|
+
def awesome_print_html
|
12
|
+
return self.awesome_print(:html => true, :indent => -3)
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
|
20
|
+
Object.send(:include, DebugBar::Ext::Object)
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'cgi'
|
2
|
+
|
3
|
+
module DebugBar
|
4
|
+
module Ext
|
5
|
+
module String
|
6
|
+
|
7
|
+
def html_escape
|
8
|
+
return CGI.escapeHTML(self)
|
9
|
+
end
|
10
|
+
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
|
16
|
+
unless( String.methods.include?(:html_escape) )
|
17
|
+
String.send(:include, DebugBar::Ext::String)
|
18
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
|
3
|
+
module DebugBar
|
4
|
+
# RecipeBook is the module namespace for the RecipeBooks provided in this
|
5
|
+
# gem. For the base RecipeBook, see RecipeBook::Base.
|
6
|
+
module RecipeBook
|
7
|
+
# The base class for all recipe subclasses. Provides common convenience
|
8
|
+
# methods for recipe use. Essentially, these are factory methods that
|
9
|
+
# lazy generate common configurable callbacks on demand.
|
10
|
+
#
|
11
|
+
# Subclasses need only to define factory instance methods that meet the
|
12
|
+
# following rules:
|
13
|
+
# 1. The method name must be the recipe name suffixed with `_recipe'. So
|
14
|
+
# the recipe
|
15
|
+
# :foo
|
16
|
+
# would have method name
|
17
|
+
# foo_recipe
|
18
|
+
# 2. Recipe factory methods <b>MUST</b> return a valid callback when no arguments
|
19
|
+
# are given, that is
|
20
|
+
# book.foo_recipe()
|
21
|
+
# must work.
|
22
|
+
# 3. The result of a recipe factory method must be a Proc object that
|
23
|
+
# conforms to the requirements of the Procs registered with +add_callback+
|
24
|
+
# on the DebugBar::Base class.
|
25
|
+
# 4. Recipe methods <i>may</i> take an additional argument, which is an
|
26
|
+
# options hash for special configuration when using +add_callback+ on
|
27
|
+
# DebugBar::Base instances. For example, one can then us
|
28
|
+
#
|
29
|
+
# For example, the following recipe renders the params hash from the given
|
30
|
+
# binding:
|
31
|
+
#
|
32
|
+
# def params_recipe(opts={})
|
33
|
+
# Proc.new do |b|
|
34
|
+
# body = (opts[:formatter] == :pretty_inspect) ? b[:params].pretty_inspect : b[:params].inspect
|
35
|
+
# ['Params', body.gsub('<','<'), :hidden => (body.length>160)]
|
36
|
+
# end
|
37
|
+
# end
|
38
|
+
#
|
39
|
+
# It could then be added to the DebugBar like so:
|
40
|
+
#
|
41
|
+
# debug_bar.add(:params)
|
42
|
+
# debug_bar.add(:params, :formatter => :pretty_inspect)
|
43
|
+
class Base
|
44
|
+
|
45
|
+
# Returns a list of recipes known to this class.
|
46
|
+
def recipes
|
47
|
+
return self.methods.select {|m| m.to_s =~ /_recipe$/}.map {|m| m.to_s.gsub(/_recipe$/,'').to_sym}
|
48
|
+
end
|
49
|
+
|
50
|
+
# Returns true if the given recipe is known.
|
51
|
+
def include?(recipe)
|
52
|
+
return self.respond_to?("#{recipe}_recipe")
|
53
|
+
end
|
54
|
+
alias_method :has_recipe?, :include?
|
55
|
+
|
56
|
+
# Generates the given recipe.
|
57
|
+
# All recipes are expected to accept no arguments, but may optionally
|
58
|
+
# take more. Optional arguments given to this method are passed through
|
59
|
+
# to the recipe method.
|
60
|
+
def recipe(recipe, *args, &block)
|
61
|
+
return self.send("#{recipe}_recipe", *args, &block)
|
62
|
+
end
|
63
|
+
alias_method :[], :recipe
|
64
|
+
|
65
|
+
# Retrieves the template search paths for this recipe instance as
|
66
|
+
# fully expanded Pathname instances.
|
67
|
+
#
|
68
|
+
# While subclasses <i>may</i> override this method, it is preferrable
|
69
|
+
# for them to use the setter (+template_search_paths=+) during instance
|
70
|
+
# initialization, as the setter sanitizes the input.
|
71
|
+
def template_search_paths
|
72
|
+
return @template_search_paths ||= []
|
73
|
+
end
|
74
|
+
|
75
|
+
# Sets the template search paths for this recipe instance, converting
|
76
|
+
# to pathname objects as necessary.
|
77
|
+
def template_search_paths=(paths)
|
78
|
+
@template_search_paths = paths.map {|path| Pathname.new(path.to_s).expand_path }
|
79
|
+
end
|
80
|
+
|
81
|
+
private
|
82
|
+
|
83
|
+
# Renders the first matching template found in the search paths. Passed
|
84
|
+
# symbols/names are automatically suffixed with 'html.erb'. The template
|
85
|
+
# name may be a symbol or string.
|
86
|
+
#
|
87
|
+
# Optionally, one can pass in :locals, which is a hash of local variables
|
88
|
+
# to render in the template.
|
89
|
+
def render_template(template, opts={})
|
90
|
+
return Erubis::Eruby.new( read_template(template) ).result( opts.fetch(:locals, {}) ).html_safe
|
91
|
+
end
|
92
|
+
|
93
|
+
# Reads the given template and returns the string of its contents.
|
94
|
+
# The template name may be either a symbol or string.
|
95
|
+
def read_template(template)
|
96
|
+
template_name = "#{template}.html.erb"
|
97
|
+
template_path = template_search_paths.map {|base_path| (base_path + template_name).expand_path}.find {|p| p.exist? && p.file?}
|
98
|
+
raise ArgumentError, "Unknown template #{template_name.inspect}. Not in #{template_search_paths.inspect}", caller if template_path.nil?
|
99
|
+
return template_path.read
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|