rails3-footnotes 4.0.0.pre
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/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
|