rails3-footnotes 4.0.0.pre.2 → 4.0.0.pre.3
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +7 -0
- data/README.md +1 -1
- data/Rakefile +3 -0
- data/lib/rails-footnotes/footnotes.rb +1 -1
- data/lib/rails-footnotes/notes/controller_note.rb +1 -1
- data/lib/rails-footnotes/notes/partials_note.rb +44 -28
- data/lib/rails-footnotes/notes/queries_note.rb +55 -120
- data/lib/rails-footnotes/notes/routes_note.rb +1 -1
- data/lib/rails-footnotes/notes/view_note.rb +59 -19
- data/lib/rails-footnotes/version.rb +1 -1
- metadata +18 -4
- data/lib/rails-footnotes/notes/general_note.rb +0 -19
data/CHANGELOG
CHANGED
@@ -1,3 +1,10 @@
|
|
1
|
+
== Footnotes v4.0.0.pre.3 (March 30, 2011)
|
2
|
+
* Merge in contributions from irjudson and jackkinsella
|
3
|
+
* fixes for partials note
|
4
|
+
* fixes for views note
|
5
|
+
* fixes for queries note
|
6
|
+
* fix a warning on routes note
|
7
|
+
|
1
8
|
== Footnotes v4.0.0.pre.2 (February 21, 2011)
|
2
9
|
* add a Railtie so the plugin will load in Rails 3 apps
|
3
10
|
* remove some internal rails3 ivars from the assigns note
|
data/README.md
CHANGED
@@ -56,7 +56,7 @@ If you have New Relic RPM installed, you may want to turn off query explains
|
|
56
56
|
|
57
57
|
Finally, you can control which notes you want to show. The default are:
|
58
58
|
|
59
|
-
Footnotes::Filter.notes = [:session, :cookies, :params, :filters, :routes, :env, :queries, :log
|
59
|
+
Footnotes::Filter.notes = [:session, :cookies, :params, :filters, :routes, :env, :queries, :log]
|
60
60
|
|
61
61
|
|
62
62
|
## Creating your own notes
|
data/Rakefile
CHANGED
@@ -27,7 +27,7 @@ module Footnotes
|
|
27
27
|
# Edit notes
|
28
28
|
@@notes = [ :controller, :view, :layout, :partials, :stylesheets, :javascripts ]
|
29
29
|
# Show notes
|
30
|
-
@@notes += [ :assigns, :session, :cookies, :params, :filters, :routes, :env, :queries, :log
|
30
|
+
@@notes += [ :assigns, :session, :cookies, :params, :filters, :routes, :env, :queries, :log ]
|
31
31
|
|
32
32
|
# Change queries for rpm note when available
|
33
33
|
# if defined?(NewRelic)
|
@@ -38,7 +38,7 @@ module Footnotes
|
|
38
38
|
else
|
39
39
|
@controller_filename=File.join(File.expand_path(Rails.root), 'app', 'controllers', "#{controller_name}.rb").sub('/controllers/controllers/', '/controllers/')
|
40
40
|
end
|
41
|
-
@controller_filename
|
41
|
+
@controller_filename if File.exist?(@controller_filename)
|
42
42
|
end
|
43
43
|
|
44
44
|
def controller_text
|
@@ -3,54 +3,70 @@ require "#{File.dirname(__FILE__)}/log_note"
|
|
3
3
|
module Footnotes
|
4
4
|
module Notes
|
5
5
|
class PartialsNote < LogNote
|
6
|
+
@@partial_subscriber = nil
|
7
|
+
cattr_accessor :partial_subscriber
|
8
|
+
|
6
9
|
def initialize(controller)
|
7
10
|
super
|
8
11
|
@controller = controller
|
9
12
|
end
|
13
|
+
|
14
|
+
def self.start!(controller)
|
15
|
+
@@partial_subscriber = Footnotes::Notes::PartialSubscriber.new
|
16
|
+
ActiveSupport::LogSubscriber.attach_to(:action_view, @@partial_subscriber)
|
17
|
+
end
|
18
|
+
|
10
19
|
def row
|
11
20
|
:edit
|
12
21
|
end
|
22
|
+
|
13
23
|
def title
|
14
24
|
"Partials (#{partials.size})"
|
15
25
|
end
|
26
|
+
|
16
27
|
def content
|
17
28
|
rows = partials.map do |filename|
|
18
29
|
href = Footnotes::Filter.prefix(filename,1,1)
|
19
30
|
shortened_name=filename.gsub(File.join(Rails.root,"app/views/"),"")
|
20
|
-
[%{<a href="#{href}">#{shortened_name}</a>},"#{@partial_times[filename].sum}ms"
|
31
|
+
[%{<a href="#{href}">#{shortened_name}</a>},"#{@partial_times[filename].sum}ms", @partial_counts[filename]]
|
21
32
|
end
|
22
33
|
mount_table(rows.unshift(%w(Partial Time Count)), :summary => "Partials for #{title}")
|
23
34
|
end
|
24
35
|
|
36
|
+
def self.load
|
37
|
+
self.loaded = true unless loaded
|
38
|
+
end
|
39
|
+
|
25
40
|
protected
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
for file in files
|
41
|
-
#TODO figure out what format got rendered if theres multiple
|
42
|
-
@partial_times[file] ||= []
|
43
|
-
@partial_times[file] << $2.to_f
|
44
|
-
@partial_counts[file] ||= 0
|
45
|
-
@partial_counts[file] += 1
|
46
|
-
partials << file unless partials.include?(file)
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|
50
|
-
end
|
51
|
-
partials.reverse
|
41
|
+
#Generate a list of partials that were rendered, also build up render times and counts.
|
42
|
+
#This is memoized so we can use its information in the title easily.
|
43
|
+
def partials
|
44
|
+
@partials ||= begin
|
45
|
+
partials = []
|
46
|
+
@partial_counts = {}
|
47
|
+
@partial_times = {}
|
48
|
+
@@partial_subscriber.events.each do |event|
|
49
|
+
partial = event.payload[:identifier]
|
50
|
+
@partial_times[partial] ||= []
|
51
|
+
@partial_times[partial] << event.duration
|
52
|
+
@partial_counts[partial] ||= 0
|
53
|
+
@partial_counts[partial] += 1
|
54
|
+
partials << partial unless partials.include?(partial)
|
52
55
|
end
|
56
|
+
partials.reverse
|
53
57
|
end
|
54
|
-
|
58
|
+
end
|
59
|
+
end
|
60
|
+
class PartialSubscriber < ActiveSupport::LogSubscriber
|
61
|
+
attr_accessor :events
|
62
|
+
def initialize
|
63
|
+
@events = Array.new
|
64
|
+
super
|
65
|
+
end
|
66
|
+
|
67
|
+
def render_partial(event)
|
68
|
+
@events << event.dup
|
69
|
+
end
|
70
|
+
end
|
55
71
|
end
|
56
72
|
end
|
@@ -3,22 +3,26 @@ require "#{File.dirname(__FILE__)}/abstract_note"
|
|
3
3
|
module Footnotes
|
4
4
|
module Notes
|
5
5
|
class QueriesNote < AbstractNote
|
6
|
-
@@alert_db_time = 0
|
6
|
+
@@alert_db_time = 16.0
|
7
7
|
@@alert_sql_number = 8
|
8
8
|
@@sql = []
|
9
9
|
@@include_when_new_relic_installed = false
|
10
10
|
@@loaded = false
|
11
|
-
|
11
|
+
@@query_subscriber = nil
|
12
12
|
cattr_accessor :sql, :alert_db_time, :alert_sql_number, :alert_explain, :loaded, :sql_explain, :instance_writter => false
|
13
13
|
cattr_reader :include_when_new_relic_installed
|
14
|
-
|
14
|
+
|
15
15
|
def self.include_when_new_relic_installed=(include_me)
|
16
16
|
@@include_when_new_relic_installed = include_me
|
17
17
|
load if include_me
|
18
18
|
end
|
19
|
-
|
19
|
+
|
20
20
|
def self.start!(controller)
|
21
21
|
@@sql = []
|
22
|
+
@@query_subscriber = Footnotes::Notes::QuerySubscriber.new
|
23
|
+
# There appears to be nothing wrong with registering with a non-existant notifier, so we just attach to both :-).
|
24
|
+
ActiveSupport::LogSubscriber.attach_to(:data_mapper, @@query_subscriber)
|
25
|
+
ActiveSupport::LogSubscriber.attach_to(:active_record, @@query_subscriber)
|
22
26
|
end
|
23
27
|
|
24
28
|
def self.to_sym
|
@@ -26,161 +30,92 @@ module Footnotes
|
|
26
30
|
end
|
27
31
|
|
28
32
|
def title
|
29
|
-
|
30
|
-
|
31
|
-
|
33
|
+
queries = @@query_subscriber.events.length
|
34
|
+
total_time = @@query_subscriber.events.inject(0){|sum, item| sum += item.payload[:duration]} / 1000.0
|
35
|
+
query_color = generate_red_color(@@query_subscriber.events.length, alert_sql_number)
|
36
|
+
db_color = generate_red_color(total_time, alert_db_time)
|
32
37
|
|
33
38
|
<<-TITLE
|
34
|
-
|
35
|
-
|
39
|
+
<span style="background-color:#{query_color}">Queries (#{queries})</span>
|
40
|
+
<span style="background-color:#{db_color}">DB (#{"%.3f" % total_time}ms)</span>
|
36
41
|
TITLE
|
37
42
|
end
|
38
43
|
|
39
44
|
def stylesheet
|
40
45
|
<<-STYLESHEET
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
46
|
+
#queries_debug_info table td, #queries_debug_info table th{border:1px solid #A00; padding:0 3px; text-align:center;}
|
47
|
+
#queries_debug_info table thead, #queries_debug_info table tbody {color:#A00;}
|
48
|
+
#queries_debug_info p {background-color:#F3F3FF; border:1px solid #CCC; margin:12px; padding:4px 6px;}
|
49
|
+
#queries_debug_info a:hover {text-decoration:underline;}
|
45
50
|
STYLESHEET
|
46
51
|
end
|
47
52
|
|
48
53
|
def content
|
49
54
|
html = ''
|
50
55
|
|
51
|
-
@@
|
52
|
-
sql_links = []
|
53
|
-
sql_links << "<a href=\"javascript:Footnotes.toggle('qtable_#{i}')\" style=\"color:#A00;\">explain</a>" if item.explain
|
54
|
-
sql_links << "<a href=\"javascript:Footnotes.toggle('qtrace_#{i}')\" style=\"color:#00A;\">trace</a>" if item.trace
|
55
|
-
|
56
|
+
@@query_subscriber.events.each_with_index do |item, i|
|
56
57
|
html << <<-HTML
|
57
|
-
|
58
|
-
|
59
|
-
<span id="explain_#{i}">#{print_query(item.query)}</span><br />
|
60
|
-
#{print_explain(i, item.explain) if item.explain}
|
61
|
-
<p id="qtrace_#{i}" style="display:none;">#{parse_trace(item.trace) if item.trace}</p><br />
|
58
|
+
#{print_name_and_time(item.payload[:name], item.payload[:duration] / 1000.0)}
|
59
|
+
<span id="explain_#{i}">#{print_query(item.payload[:sql])}</span><br />
|
62
60
|
HTML
|
63
61
|
end
|
64
62
|
|
65
63
|
return html
|
66
64
|
end
|
67
|
-
|
65
|
+
|
68
66
|
def self.load
|
69
|
-
|
70
|
-
if !loaded and included? and defined?(ActiveRecord)
|
71
|
-
ActiveRecord::ConnectionAdapters::AbstractAdapter.send :include, Footnotes::Extensions::AbstractAdapter
|
72
|
-
ActiveRecord::ConnectionAdapters.local_constants.each do |adapter|
|
73
|
-
next unless adapter =~ /.*[^Abstract]Adapter$/
|
74
|
-
next if adapter =~ /(SQLite|Salesforce)Adapter$/
|
75
|
-
eval("ActiveRecord::ConnectionAdapters::#{adapter}").send :include, Footnotes::Extensions::QueryAnalyzer
|
76
|
-
self.loaded = true
|
77
|
-
end
|
78
|
-
end
|
67
|
+
self.loaded = true unless loaded
|
79
68
|
end
|
80
|
-
|
81
|
-
protected
|
82
|
-
def parse_explain(results)
|
83
|
-
table = []
|
84
|
-
table << results.fetch_fields.map(&:name)
|
85
|
-
results.each do |row|
|
86
|
-
table << row
|
87
|
-
end
|
88
|
-
table
|
89
|
-
end
|
90
|
-
|
91
|
-
def parse_trace(trace)
|
92
|
-
trace.map do |t|
|
93
|
-
s = t.split(':')
|
94
|
-
%[<a href="#{escape(Footnotes::Filter.prefix("#{Rails.root.to_s}/#{s[0]}", s[1].to_i, 1))}">#{escape(t)}</a><br />]
|
95
|
-
end.join
|
96
|
-
end
|
97
|
-
|
98
|
-
def print_name_and_time(name, time)
|
99
|
-
"<span style='background-color:#{generate_red_color(time, alert_ratio)}'>#{escape(name || 'SQL')} (#{sprintf('%f', time)}s)</span>"
|
100
|
-
end
|
101
69
|
|
102
|
-
|
103
|
-
|
104
|
-
|
70
|
+
protected
|
71
|
+
def print_name_and_time(name, time)
|
72
|
+
"<span style='background-color:#{generate_red_color(time, alert_ratio)}'>#{escape(name || 'SQL')} (#{'%.3fms' % time})</span>"
|
73
|
+
end
|
105
74
|
|
106
|
-
|
107
|
-
|
108
|
-
|
75
|
+
def print_query(query)
|
76
|
+
escape(query.to_s.gsub(/(\s)+/, ' ').gsub('`', ''))
|
77
|
+
end
|
109
78
|
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
79
|
+
def generate_red_color(value, alert)
|
80
|
+
c = ((value.to_f/alert).to_i - 1) * 16
|
81
|
+
c = 0 if c < 0
|
82
|
+
c = 15 if c > 15
|
114
83
|
|
115
|
-
|
116
|
-
|
117
|
-
|
84
|
+
c = (15-c).to_s(16)
|
85
|
+
"#ff#{c*4}"
|
86
|
+
end
|
118
87
|
|
119
|
-
|
120
|
-
|
121
|
-
|
88
|
+
def alert_ratio
|
89
|
+
alert_db_time / alert_sql_number
|
90
|
+
end
|
122
91
|
|
123
92
|
end
|
124
|
-
end
|
125
|
-
|
126
|
-
module Extensions
|
127
|
-
class Sql
|
128
|
-
attr_accessor :type, :name, :time, :query, :explain, :trace
|
129
93
|
|
130
|
-
|
131
|
-
|
132
|
-
@name = name
|
133
|
-
@time = time
|
134
|
-
@query = query
|
135
|
-
@explain = explain
|
94
|
+
class QuerySubscriber < ActiveSupport::LogSubscriber
|
95
|
+
attr_accessor :events
|
136
96
|
|
137
|
-
|
138
|
-
|
139
|
-
@trace = Kernel.caller.collect(&:strip).select{|i| i.gsub!(/^#{Rails.root}\//im, '') }[2..-1]
|
97
|
+
def self.runtime=(value)
|
98
|
+
Thread.current["orm_sql_runtime"] = value
|
140
99
|
end
|
141
|
-
end
|
142
100
|
|
143
|
-
|
144
|
-
|
145
|
-
base.class_eval do
|
146
|
-
alias_method_chain :execute, :analyzer
|
147
|
-
end
|
101
|
+
def self.runtime
|
102
|
+
Thread.current["orm_sql_runtime"] ||= 0
|
148
103
|
end
|
149
104
|
|
150
|
-
def
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
if query =~ /^(select|create|update|delete)\b/i
|
155
|
-
type = $&.downcase.to_sym
|
156
|
-
explain = nil
|
157
|
-
|
158
|
-
if adapter_name == 'MySQL' && type == :select && Footnotes::Notes::QueriesNote.sql_explain
|
159
|
-
log_silence do
|
160
|
-
explain = execute_without_analyzer("EXPLAIN #{query}", name)
|
161
|
-
end
|
162
|
-
end
|
163
|
-
Footnotes::Notes::QueriesNote.sql << Footnotes::Extensions::Sql.new(type, name, time, query, explain)
|
164
|
-
end
|
105
|
+
def self.reset_runtime
|
106
|
+
rt, self.runtime = runtime, 0
|
107
|
+
rt
|
108
|
+
end
|
165
109
|
|
166
|
-
|
110
|
+
def initialize
|
111
|
+
@events = Array.new
|
112
|
+
super
|
167
113
|
end
|
168
|
-
end
|
169
114
|
|
170
|
-
|
171
|
-
|
172
|
-
result = nil
|
173
|
-
if @logger
|
174
|
-
@logger.silence do
|
175
|
-
result = yield
|
176
|
-
end
|
177
|
-
else
|
178
|
-
result = yield
|
179
|
-
end
|
180
|
-
result
|
115
|
+
def sql(event)
|
116
|
+
@events << event.dup
|
181
117
|
end
|
182
118
|
end
|
183
|
-
|
184
119
|
end
|
185
120
|
end
|
186
121
|
|
@@ -48,7 +48,7 @@ module Footnotes
|
|
48
48
|
#
|
49
49
|
def filtered_routes(filter = {})
|
50
50
|
return [] unless filter.is_a?(Hash)
|
51
|
-
return routes.reject do |r|
|
51
|
+
return routes.reject do |r|
|
52
52
|
filter_diff = filter.diff(r.requirements)
|
53
53
|
route_diff = r.requirements.diff(filter)
|
54
54
|
(filter_diff == filter) || (filter_diff != route_diff)
|
@@ -3,33 +3,73 @@ require "#{File.dirname(__FILE__)}/abstract_note"
|
|
3
3
|
module Footnotes
|
4
4
|
module Notes
|
5
5
|
class ViewNote < AbstractNote
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
end
|
6
|
+
@@alert_time = 500.0
|
7
|
+
@@loaded = false
|
8
|
+
@@view_subscriber = nil
|
10
9
|
|
11
|
-
|
12
|
-
|
13
|
-
|
10
|
+
def initialize(controller)
|
11
|
+
super
|
12
|
+
@controller = controller
|
13
|
+
end
|
14
14
|
|
15
|
-
|
16
|
-
|
17
|
-
|
15
|
+
def self.start!(controller)
|
16
|
+
@@view_subscriber = Footnotes::Notes::ViewSubscriber.new
|
17
|
+
ActiveSupport::LogSubscriber.attach_to(:action_view, @@view_subscriber)
|
18
|
+
end
|
18
19
|
|
19
|
-
|
20
|
-
|
21
|
-
|
20
|
+
def self.to_sym
|
21
|
+
:views
|
22
|
+
end
|
22
23
|
|
23
|
-
|
24
|
+
def title
|
25
|
+
total_time = @@view_subscriber.events.select{ |e| e.name =~ /render_template/ }[0].duration
|
26
|
+
"<span style=\"background-color:#{generate_red_color(total_time)}\">View Render (#{"%.3f" % total_time}ms)</span>"
|
27
|
+
end
|
24
28
|
|
25
|
-
|
26
|
-
|
27
|
-
|
29
|
+
def content
|
30
|
+
html = ''
|
31
|
+
page = @@view_subscriber.events.select{ |e| e.name =~ /render_template/ }[0]
|
32
|
+
partials = @@view_subscriber.events.select{ |e| e.name =~ /render_partial/ }
|
33
|
+
partial_time = partials.inject(0) {|sum, item| sum += item.duration}
|
34
|
+
|
35
|
+
view = page.payload[:identifier].gsub(File.join(Rails.root,"app/views/"),"")
|
36
|
+
layout = page.payload[:layout].gsub(File.join(Rails.root,"app/views/"),"")
|
28
37
|
|
29
|
-
|
30
|
-
|
38
|
+
rows = [["View", "Layout", "View Render (ms)", "Partial(s) Render (ms)", "Total Render (ms)"],
|
39
|
+
[escape(view), escape(layout), "#{'%.3f' % (page.duration - partial_time)}",
|
40
|
+
"<a href=\"#\" onclick=\"Footnotes.hideAllAndToggle('partials_debug_info');return false;\">#{'%.3f' % partial_time}</a>",
|
41
|
+
"#{'%.3f' % page.duration}"]]
|
42
|
+
|
43
|
+
puts rows.inspect
|
44
|
+
|
45
|
+
mount_table(rows)
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.load
|
49
|
+
self.loaded = true unless loaded
|
50
|
+
end
|
51
|
+
|
52
|
+
def generate_red_color(value)
|
53
|
+
if value > @@alert_time
|
54
|
+
"#f00"
|
55
|
+
else
|
56
|
+
"#aaa"
|
57
|
+
end
|
58
|
+
end
|
31
59
|
end
|
32
60
|
|
61
|
+
class ViewSubscriber < ActiveSupport::LogSubscriber
|
62
|
+
attr_accessor :events
|
63
|
+
def initialize
|
64
|
+
@events = Array.new
|
65
|
+
super
|
66
|
+
end
|
67
|
+
|
68
|
+
def render_template(event)
|
69
|
+
@events << event.dup
|
70
|
+
end
|
71
|
+
alias :render_partial :render_template
|
72
|
+
alias :render_collection :render_template
|
33
73
|
end
|
34
74
|
end
|
35
75
|
end
|
metadata
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rails3-footnotes
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 1923831855
|
5
5
|
prerelease: true
|
6
6
|
segments:
|
7
7
|
- 4
|
8
8
|
- 0
|
9
9
|
- 0
|
10
10
|
- pre
|
11
|
-
-
|
12
|
-
version: 4.0.0.pre.
|
11
|
+
- 3
|
12
|
+
version: 4.0.0.pre.3
|
13
13
|
platform: ruby
|
14
14
|
authors:
|
15
15
|
- "Andr\xC3\xA9 Arko"
|
@@ -35,6 +35,21 @@ dependencies:
|
|
35
35
|
version: "3.0"
|
36
36
|
type: :runtime
|
37
37
|
version_requirements: *id001
|
38
|
+
- !ruby/object:Gem::Dependency
|
39
|
+
name: bundler
|
40
|
+
prerelease: false
|
41
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
42
|
+
none: false
|
43
|
+
requirements:
|
44
|
+
- - ~>
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
hash: 15
|
47
|
+
segments:
|
48
|
+
- 1
|
49
|
+
- 0
|
50
|
+
version: "1.0"
|
51
|
+
type: :development
|
52
|
+
version_requirements: *id002
|
38
53
|
description: Adds footnotes to each page in your Rails app, containing useful development information as well as links to edit the controllers and views in your editor.
|
39
54
|
email: andre@arko.net
|
40
55
|
executables: []
|
@@ -57,7 +72,6 @@ files:
|
|
57
72
|
- lib/rails-footnotes/notes/env_note.rb
|
58
73
|
- lib/rails-footnotes/notes/files_note.rb
|
59
74
|
- lib/rails-footnotes/notes/filters_note.rb
|
60
|
-
- lib/rails-footnotes/notes/general_note.rb
|
61
75
|
- lib/rails-footnotes/notes/javascripts_note.rb
|
62
76
|
- lib/rails-footnotes/notes/layout_note.rb
|
63
77
|
- lib/rails-footnotes/notes/log_note.rb
|
@@ -1,19 +0,0 @@
|
|
1
|
-
require "#{File.dirname(__FILE__)}/abstract_note"
|
2
|
-
|
3
|
-
module Footnotes
|
4
|
-
module Notes
|
5
|
-
class GeneralNote < AbstractNote
|
6
|
-
def title
|
7
|
-
'General Debug'
|
8
|
-
end
|
9
|
-
|
10
|
-
def legend
|
11
|
-
'General (id="general_debug_info")'
|
12
|
-
end
|
13
|
-
|
14
|
-
def content
|
15
|
-
'You can use this tab to debug other parts of your application, for example Javascript.'
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|