rails3-footnotes 4.0.0.pre
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +81 -0
- data/MIT-LICENSE +21 -0
- data/README.md +129 -0
- data/Rakefile +33 -0
- data/lib/rails-footnotes/backtracer.rb +36 -0
- data/lib/rails-footnotes/footnotes.rb +365 -0
- data/lib/rails-footnotes/notes/abstract_note.rb +179 -0
- data/lib/rails-footnotes/notes/assigns_note.rb +60 -0
- data/lib/rails-footnotes/notes/controller_note.rb +72 -0
- data/lib/rails-footnotes/notes/cookies_note.rb +19 -0
- data/lib/rails-footnotes/notes/env_note.rb +26 -0
- data/lib/rails-footnotes/notes/files_note.rb +44 -0
- data/lib/rails-footnotes/notes/filters_note.rb +53 -0
- data/lib/rails-footnotes/notes/general_note.rb +19 -0
- data/lib/rails-footnotes/notes/javascripts_note.rb +16 -0
- data/lib/rails-footnotes/notes/layout_note.rb +28 -0
- data/lib/rails-footnotes/notes/log_note.rb +47 -0
- data/lib/rails-footnotes/notes/params_note.rb +19 -0
- data/lib/rails-footnotes/notes/partials_note.rb +56 -0
- data/lib/rails-footnotes/notes/queries_note.rb +187 -0
- data/lib/rails-footnotes/notes/routes_note.rb +63 -0
- data/lib/rails-footnotes/notes/rpm_note.rb +30 -0
- data/lib/rails-footnotes/notes/session_note.rb +29 -0
- data/lib/rails-footnotes/notes/stylesheets_note.rb +16 -0
- data/lib/rails-footnotes/notes/view_note.rb +35 -0
- data/lib/rails-footnotes.rb +22 -0
- data/test/footnotes_test.rb +207 -0
- data/test/notes/abstract_note_test.rb +107 -0
- data/test/test_helper.rb +10 -0
- metadata +100 -0
@@ -0,0 +1,187 @@
|
|
1
|
+
require "#{File.dirname(__FILE__)}/abstract_note"
|
2
|
+
|
3
|
+
module Footnotes
|
4
|
+
module Notes
|
5
|
+
class QueriesNote < AbstractNote
|
6
|
+
@@alert_db_time = 0.16
|
7
|
+
@@alert_sql_number = 8
|
8
|
+
@@sql = []
|
9
|
+
@@include_when_new_relic_installed = false
|
10
|
+
@@loaded = false
|
11
|
+
|
12
|
+
cattr_accessor :sql, :alert_db_time, :alert_sql_number, :alert_explain, :loaded, :sql_explain, :instance_writter => false
|
13
|
+
cattr_reader :include_when_new_relic_installed
|
14
|
+
|
15
|
+
def self.include_when_new_relic_installed=(include_me)
|
16
|
+
@@include_when_new_relic_installed = include_me
|
17
|
+
load if include_me
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.start!(controller)
|
21
|
+
@@sql = []
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.to_sym
|
25
|
+
:queries
|
26
|
+
end
|
27
|
+
|
28
|
+
def title
|
29
|
+
db_time = @@sql.inject(0){|sum, item| sum += item.time }
|
30
|
+
query_color = generate_red_color(@@sql.length, alert_sql_number)
|
31
|
+
db_color = generate_red_color(db_time, alert_db_time)
|
32
|
+
|
33
|
+
<<-TITLE
|
34
|
+
<span style="background-color:#{query_color}">Queries (#{@@sql.length})</span>
|
35
|
+
<span style="background-color:#{db_color}">DB (#{"%.6f" % db_time}s)</span>
|
36
|
+
TITLE
|
37
|
+
end
|
38
|
+
|
39
|
+
def stylesheet
|
40
|
+
<<-STYLESHEET
|
41
|
+
#queries_debug_info table td, #queries_debug_info table th{border:1px solid #A00; padding:0 3px; text-align:center;}
|
42
|
+
#queries_debug_info table thead, #queries_debug_info table tbody {color:#A00;}
|
43
|
+
#queries_debug_info p {background-color:#F3F3FF; border:1px solid #CCC; margin:12px; padding:4px 6px;}
|
44
|
+
#queries_debug_info a:hover {text-decoration:underline;}
|
45
|
+
STYLESHEET
|
46
|
+
end
|
47
|
+
|
48
|
+
def content
|
49
|
+
html = ''
|
50
|
+
|
51
|
+
@@sql.each_with_index do |item, i|
|
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
|
+
html << <<-HTML
|
57
|
+
<b id="qtitle_#{i}">#{escape(item.type.to_s.upcase)}</b> (#{sql_links.join(' | ')})<br />
|
58
|
+
#{print_name_and_time(item.name, item.time)}<br />
|
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 />
|
62
|
+
HTML
|
63
|
+
end
|
64
|
+
|
65
|
+
return html
|
66
|
+
end
|
67
|
+
|
68
|
+
def self.load
|
69
|
+
#only include when NewRelic is installed if configured to do so
|
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
|
79
|
+
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
|
+
|
102
|
+
def print_query(query)
|
103
|
+
escape(query.to_s.gsub(/(\s)+/, ' ').gsub('`', ''))
|
104
|
+
end
|
105
|
+
|
106
|
+
def print_explain(i, explain)
|
107
|
+
mount_table(parse_explain(explain), :id => "qtable_#{i}", :style => 'margin:10px;display:none;', :summary => "Debug information for #{title}")
|
108
|
+
end
|
109
|
+
|
110
|
+
def generate_red_color(value, alert)
|
111
|
+
c = ((value.to_f/alert).to_i - 1) * 16
|
112
|
+
c = 0 if c < 0
|
113
|
+
c = 15 if c > 15
|
114
|
+
|
115
|
+
c = (15-c).to_s(16)
|
116
|
+
"#ff#{c*4}"
|
117
|
+
end
|
118
|
+
|
119
|
+
def alert_ratio
|
120
|
+
alert_db_time / alert_sql_number
|
121
|
+
end
|
122
|
+
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
module Extensions
|
127
|
+
class Sql
|
128
|
+
attr_accessor :type, :name, :time, :query, :explain, :trace
|
129
|
+
|
130
|
+
def initialize(type, name, time, query, explain)
|
131
|
+
@type = type
|
132
|
+
@name = name
|
133
|
+
@time = time
|
134
|
+
@query = query
|
135
|
+
@explain = explain
|
136
|
+
|
137
|
+
# Strip, select those ones from app and reject first two, because they
|
138
|
+
# are from the plugin
|
139
|
+
@trace = Kernel.caller.collect(&:strip).select{|i| i.gsub!(/^#{Rails.root}\//im, '') }[2..-1]
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
module QueryAnalyzer
|
144
|
+
def self.included(base)
|
145
|
+
base.class_eval do
|
146
|
+
alias_method_chain :execute, :analyzer
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
def execute_with_analyzer(query, name = nil)
|
151
|
+
query_results = nil
|
152
|
+
time = Benchmark.realtime { query_results = execute_without_analyzer(query, name) }
|
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
|
165
|
+
|
166
|
+
query_results
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
module AbstractAdapter
|
171
|
+
def log_silence
|
172
|
+
result = nil
|
173
|
+
if @logger
|
174
|
+
@logger.silence do
|
175
|
+
result = yield
|
176
|
+
end
|
177
|
+
else
|
178
|
+
result = yield
|
179
|
+
end
|
180
|
+
result
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
Footnotes::Notes::QueriesNote.load
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require "#{File.dirname(__FILE__)}/abstract_note"
|
2
|
+
|
3
|
+
module Footnotes
|
4
|
+
module Notes
|
5
|
+
class RoutesNote < AbstractNote
|
6
|
+
def initialize(controller)
|
7
|
+
@controller = controller
|
8
|
+
@parsed_routes = parse_routes
|
9
|
+
end
|
10
|
+
|
11
|
+
def legend
|
12
|
+
"Routes for #{@controller.class.to_s}"
|
13
|
+
end
|
14
|
+
|
15
|
+
def content
|
16
|
+
mount_table(@parsed_routes.unshift([:path, :name, :options, :requirements]), :summary => "Debug information for #{title}")
|
17
|
+
end
|
18
|
+
|
19
|
+
protected
|
20
|
+
def parse_routes
|
21
|
+
routes_with_name = Rails.application.routes.named_routes.to_a.flatten
|
22
|
+
|
23
|
+
return Rails.application.routes.filtered_routes(:controller => @controller.controller_path).collect do |route|
|
24
|
+
# Catch routes name if exists
|
25
|
+
i = routes_with_name.index(route)
|
26
|
+
name = i ? routes_with_name[i-1].to_s : ''
|
27
|
+
|
28
|
+
# Catch segments requirements
|
29
|
+
req = {}
|
30
|
+
if Rails.version < '3.0'
|
31
|
+
route.segments.each do |segment|
|
32
|
+
next unless segment.is_a?(ActionController::Routing::DynamicSegment) && segment.regexp
|
33
|
+
req[segment.key.to_sym] = segment.regexp
|
34
|
+
end
|
35
|
+
[escape(name), route.segments.join, route.requirements.reject{|key,value| key == :controller}.inspect, req.inspect]
|
36
|
+
else
|
37
|
+
req = route.conditions
|
38
|
+
[escape(name), route.conditions.keys.join, route.requirements.reject{|key,value| key == :controller}.inspect, req.inspect]
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
module Extensions
|
46
|
+
module Routes
|
47
|
+
# Filter routes according to the filter sent
|
48
|
+
#
|
49
|
+
def filtered_routes(filter = {})
|
50
|
+
return [] unless filter.is_a?(Hash)
|
51
|
+
return routes.reject do |r|
|
52
|
+
filter_diff = filter.diff(r.requirements)
|
53
|
+
route_diff = r.requirements.diff(filter)
|
54
|
+
(filter_diff == filter) || (filter_diff != route_diff)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
if Footnotes::Notes::RoutesNote.included?
|
62
|
+
ActionController::Routing::RouteSet.send :include, Footnotes::Extensions::Routes
|
63
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require "#{File.dirname(__FILE__)}/abstract_note"
|
2
|
+
|
3
|
+
if defined?(NewRelic)
|
4
|
+
module Footnotes
|
5
|
+
module Notes
|
6
|
+
class RpmNote < AbstractNote
|
7
|
+
def initialize(controller)
|
8
|
+
@rpm_id=NewRelic::Agent.instance.transaction_sampler.current_sample_id
|
9
|
+
end
|
10
|
+
|
11
|
+
def row
|
12
|
+
:edit
|
13
|
+
end
|
14
|
+
|
15
|
+
def link
|
16
|
+
#{:controller => 'newrelic', :action => 'show_sample_detail', :id => @rpm_id}
|
17
|
+
"/newrelic/show_sample_detail/#{@rpm_id}" if @rpm_id
|
18
|
+
end
|
19
|
+
|
20
|
+
def valid?
|
21
|
+
if defined?(NewRelic::Control)
|
22
|
+
!NewRelic::Control.instance['skip_developer_route']
|
23
|
+
else
|
24
|
+
!NewRelic::Config.instance['skip_developer_route']
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require "#{File.dirname(__FILE__)}/abstract_note"
|
2
|
+
|
3
|
+
module Footnotes
|
4
|
+
module Notes
|
5
|
+
class SessionNote < AbstractNote
|
6
|
+
def initialize(controller)
|
7
|
+
session = controller.session
|
8
|
+
if session
|
9
|
+
if session.respond_to? :to_hash
|
10
|
+
# rails >= 2.3
|
11
|
+
session = session.to_hash
|
12
|
+
else
|
13
|
+
#rails < 2.3
|
14
|
+
session = session.data
|
15
|
+
end
|
16
|
+
end
|
17
|
+
@session = (session || {}).symbolize_keys
|
18
|
+
end
|
19
|
+
|
20
|
+
def title
|
21
|
+
"Session (#{@session.length})"
|
22
|
+
end
|
23
|
+
|
24
|
+
def content
|
25
|
+
mount_table_for_hash(@session, :summary => "Debug information for #{title}")
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require "#{File.dirname(__FILE__)}/files_note"
|
2
|
+
|
3
|
+
module Footnotes
|
4
|
+
module Notes
|
5
|
+
class StylesheetsNote < FilesNote
|
6
|
+
def title
|
7
|
+
"Stylesheets (#{@files.length})"
|
8
|
+
end
|
9
|
+
|
10
|
+
protected
|
11
|
+
def scan_text(text)
|
12
|
+
text.scan(/<link[^>]+href\s*=\s*['"]([^>?'"]+\.css)/im).flatten
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require "#{File.dirname(__FILE__)}/abstract_note"
|
2
|
+
|
3
|
+
module Footnotes
|
4
|
+
module Notes
|
5
|
+
class ViewNote < AbstractNote
|
6
|
+
def initialize(controller)
|
7
|
+
@controller = controller
|
8
|
+
@template = controller.instance_variable_get(:@template)
|
9
|
+
end
|
10
|
+
|
11
|
+
def row
|
12
|
+
:edit
|
13
|
+
end
|
14
|
+
|
15
|
+
def link
|
16
|
+
escape(Footnotes::Filter.prefix(filename, 1, 1))
|
17
|
+
end
|
18
|
+
|
19
|
+
def valid?
|
20
|
+
prefix? && first_render?
|
21
|
+
end
|
22
|
+
|
23
|
+
protected
|
24
|
+
|
25
|
+
def first_render?
|
26
|
+
@template.instance_variable_get(:@_first_render)
|
27
|
+
end
|
28
|
+
|
29
|
+
def filename
|
30
|
+
@filename ||= @template.instance_variable_get(:@_first_render).filename
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Footnotes
|
2
|
+
|
3
|
+
# The footnotes are applied by default to all actions. You can change this
|
4
|
+
# behavior commenting the after_filter line below and putting it in Your
|
5
|
+
# application. Then you can cherrypick in which actions it will appear.
|
6
|
+
#
|
7
|
+
module RailsFootnotesExtension
|
8
|
+
def self.included(base)
|
9
|
+
base.prepend_before_filter Footnotes::BeforeFilter
|
10
|
+
base.after_filter Footnotes::AfterFilter
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.run!
|
15
|
+
require 'rails-footnotes/footnotes'
|
16
|
+
require 'rails-footnotes/backtracer'
|
17
|
+
|
18
|
+
Dir[File.join(File.dirname(__FILE__), 'rails-footnotes', 'notes', '*.rb')].each { |note| require note }
|
19
|
+
|
20
|
+
ActionController::Base.send(:include, RailsFootnotesExtension)
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,207 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
require 'action_controller'
|
4
|
+
require 'action_controller/test_case'
|
5
|
+
|
6
|
+
class FootnotesController < ActionController::Base; attr_accessor :template, :performed_render; end
|
7
|
+
|
8
|
+
module Footnotes::Notes
|
9
|
+
class TestNote < AbstractNote
|
10
|
+
def self.to_sym; :test; end
|
11
|
+
def valid?; true; end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class FootnotesTest < Test::Unit::TestCase
|
16
|
+
def setup
|
17
|
+
@controller = FootnotesController.new
|
18
|
+
@controller.template = Object.new
|
19
|
+
@controller.request = ActionController::TestRequest.new
|
20
|
+
@controller.response = ActionController::TestResponse.new
|
21
|
+
@controller.response.body = $html.dup
|
22
|
+
@controller.params = {}
|
23
|
+
|
24
|
+
Footnotes::Filter.notes = [ :test ]
|
25
|
+
Footnotes::Filter.multiple_notes = false
|
26
|
+
@footnotes = Footnotes::Filter.new(@controller)
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_footnotes_controller
|
30
|
+
index = @controller.response.body.index(/This is the HTML page/)
|
31
|
+
assert_equal 334, index
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_foonotes_included
|
35
|
+
footnotes_perform!
|
36
|
+
assert_not_equal $html, @controller.response.body
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_footnotes_not_included_when_request_is_xhr
|
40
|
+
@controller.request.env['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest'
|
41
|
+
@controller.request.env['HTTP_ACCEPT'] = 'text/javascript, text/html, application/xml, text/xml, */*'
|
42
|
+
|
43
|
+
footnotes_perform!
|
44
|
+
assert_equal $html, @controller.response.body
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_footnotes_not_included_when_content_type_is_javascript
|
48
|
+
@controller.response.headers['Content-Type'] = 'text/javascript'
|
49
|
+
|
50
|
+
footnotes_perform!
|
51
|
+
assert_equal $html, @controller.response.body
|
52
|
+
end
|
53
|
+
|
54
|
+
def test_footnotes_included_when_content_type_is_html
|
55
|
+
@controller.response.headers['Content-Type'] = 'text/html'
|
56
|
+
|
57
|
+
footnotes_perform!
|
58
|
+
assert_not_equal $html, @controller.response.body
|
59
|
+
end
|
60
|
+
|
61
|
+
def test_footnotes_included_when_content_type_is_nil
|
62
|
+
footnotes_perform!
|
63
|
+
assert_not_equal $html, @controller.response.body
|
64
|
+
end
|
65
|
+
|
66
|
+
def test_not_included_when_body_is_not_a_string
|
67
|
+
@controller.response.body = Proc.new{ Time.now }
|
68
|
+
assert_nothing_raised do
|
69
|
+
footnotes_perform!
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def test_footnotes_prefix
|
74
|
+
assert_equal 'txmt://open?url=file://%s&line=%d&column=%d', Footnotes::Filter.prefix
|
75
|
+
assert_equal 'txmt://open?url=file://file&line=0&column=0', Footnotes::Filter.prefix('file', 0, 0)
|
76
|
+
assert_equal 'txmt://open?url=file://file&line=10&column=10', Footnotes::Filter.prefix('file', 10, 10)
|
77
|
+
assert_equal 'txmt://open?url=file://file&line=10&column=10', Footnotes::Filter.prefix('file', 10, 10, 10)
|
78
|
+
assert_equal 'txmt://open?url=file://file&line=10&column=10', Footnotes::Filter.prefix('file', '10', '10')
|
79
|
+
end
|
80
|
+
|
81
|
+
def test_notes_are_initialized
|
82
|
+
footnotes_perform!
|
83
|
+
test_note = @footnotes.instance_variable_get('@notes').first
|
84
|
+
assert_equal 'Footnotes::Notes::TestNote', test_note.class.name
|
85
|
+
assert_equal :test, test_note.to_sym
|
86
|
+
end
|
87
|
+
|
88
|
+
def test_notes_links
|
89
|
+
note = Footnotes::Notes::TestNote.new
|
90
|
+
note.expects(:row).times(2)
|
91
|
+
@footnotes.instance_variable_set(:@notes, [note])
|
92
|
+
footnotes_perform!
|
93
|
+
end
|
94
|
+
|
95
|
+
def test_notes_fieldset
|
96
|
+
note = Footnotes::Notes::TestNote.new
|
97
|
+
note.expects(:has_fieldset?).times(3)
|
98
|
+
@footnotes.instance_variable_set(:@notes, [note])
|
99
|
+
footnotes_perform!
|
100
|
+
end
|
101
|
+
|
102
|
+
def test_multiple_notes
|
103
|
+
Footnotes::Filter.multiple_notes = true
|
104
|
+
note = Footnotes::Notes::TestNote.new
|
105
|
+
note.expects(:has_fieldset?).times(2)
|
106
|
+
@footnotes.instance_variable_set(:@notes, [note])
|
107
|
+
footnotes_perform!
|
108
|
+
end
|
109
|
+
|
110
|
+
def test_notes_are_reset
|
111
|
+
note = Footnotes::Notes::TestNote.new
|
112
|
+
note.class.expects(:close!)
|
113
|
+
@footnotes.instance_variable_set(:@notes, [note])
|
114
|
+
@footnotes.send(:close!, @controller)
|
115
|
+
end
|
116
|
+
|
117
|
+
def test_links_helper
|
118
|
+
note = Footnotes::Notes::TestNote.new
|
119
|
+
assert_equal '<a href="#" onclick="">Test</a>', @footnotes.send(:link_helper, note)
|
120
|
+
|
121
|
+
note.expects(:link).times(1).returns(:link)
|
122
|
+
assert_equal '<a href="link" onclick="">Test</a>', @footnotes.send(:link_helper, note)
|
123
|
+
end
|
124
|
+
|
125
|
+
def test_links_helper_has_fieldset?
|
126
|
+
note = Footnotes::Notes::TestNote.new
|
127
|
+
note.expects(:has_fieldset?).times(1).returns(true)
|
128
|
+
assert_equal '<a href="#" onclick="Footnotes.hideAllAndToggle(\'test_debug_info\');return false;">Test</a>', @footnotes.send(:link_helper, note)
|
129
|
+
end
|
130
|
+
|
131
|
+
def test_links_helper_onclick
|
132
|
+
note = Footnotes::Notes::TestNote.new
|
133
|
+
note.expects(:onclick).times(2).returns(:onclick)
|
134
|
+
assert_equal '<a href="#" onclick="onclick">Test</a>', @footnotes.send(:link_helper, note)
|
135
|
+
|
136
|
+
note.expects(:has_fieldset?).times(1).returns(true)
|
137
|
+
assert_equal '<a href="#" onclick="onclick">Test</a>', @footnotes.send(:link_helper, note)
|
138
|
+
end
|
139
|
+
|
140
|
+
def test_insert_style
|
141
|
+
@controller.response.body = "<head></head><split><body></body>"
|
142
|
+
@footnotes = Footnotes::Filter.new(@controller)
|
143
|
+
footnotes_perform!
|
144
|
+
assert @controller.response.body.split('<split>').first.include?('<!-- Footnotes Style -->')
|
145
|
+
end
|
146
|
+
|
147
|
+
def test_insert_footnotes_inside_body
|
148
|
+
@controller.response.body = "<head></head><split><body></body>"
|
149
|
+
@footnotes = Footnotes::Filter.new(@controller)
|
150
|
+
footnotes_perform!
|
151
|
+
assert @controller.response.body.split('<split>').last.include?('<!-- End Footnotes -->')
|
152
|
+
end
|
153
|
+
|
154
|
+
def test_insert_footnotes_inside_holder
|
155
|
+
@controller.response.body = "<head></head><split><div id='footnotes_holder'></div>"
|
156
|
+
@footnotes = Footnotes::Filter.new(@controller)
|
157
|
+
footnotes_perform!
|
158
|
+
assert @controller.response.body.split('<split>').last.include?('<!-- End Footnotes -->')
|
159
|
+
end
|
160
|
+
|
161
|
+
def test_insert_text
|
162
|
+
@footnotes.send(:insert_text, :after, /<head>/, "Graffiti")
|
163
|
+
after = " <head>Graffiti"
|
164
|
+
assert_equal after, @controller.response.body.split("\n")[2]
|
165
|
+
|
166
|
+
@footnotes.send(:insert_text, :before, /<\/body>/, "Notes")
|
167
|
+
after = " Notes</body>"
|
168
|
+
assert_equal after, @controller.response.body.split("\n")[12]
|
169
|
+
end
|
170
|
+
|
171
|
+
protected
|
172
|
+
# First we make sure that footnotes will perform (long life to mocha!)
|
173
|
+
# Then we call add_footnotes!
|
174
|
+
#
|
175
|
+
def footnotes_perform!
|
176
|
+
template_expects('html')
|
177
|
+
@controller.performed_render = true
|
178
|
+
|
179
|
+
Footnotes::Filter.start!(@controller)
|
180
|
+
@footnotes.add_footnotes!
|
181
|
+
end
|
182
|
+
|
183
|
+
def template_expects(format)
|
184
|
+
if @controller.template.respond_to?(:template_format)
|
185
|
+
@controller.template.expects(:template_format).returns(format)
|
186
|
+
else
|
187
|
+
@controller.template.expects(:format).returns(format)
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
$html = <<HTML
|
193
|
+
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
194
|
+
<html>
|
195
|
+
<head>
|
196
|
+
<title>HTML to XHTML Example: HTML page</title>
|
197
|
+
<link rel="Stylesheet" href="htmltohxhtml.css" type="text/css" media="screen">
|
198
|
+
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
|
199
|
+
</head>
|
200
|
+
<body>
|
201
|
+
<p>This is the HTML page. It works and is encoded just like any HTML page you
|
202
|
+
have previously done. View <a href="htmltoxhtml2.htm">the XHTML version</a> of
|
203
|
+
this page to view the difference between HTML and XHTML.</p>
|
204
|
+
<p>You will be glad to know that no changes need to be made to any of your CSS files.</p>
|
205
|
+
</body>
|
206
|
+
</html>
|
207
|
+
HTML
|