scharfie-rails-footnotes 3.4.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,16 @@
1
+ require "#{File.dirname(__FILE__)}/files_note"
2
+
3
+ module Footnotes
4
+ module Notes
5
+ class JavascriptsNote < FilesNote
6
+ def title
7
+ "Javascripts (#{@files.length})"
8
+ end
9
+
10
+ protected
11
+ def scan_text(text)
12
+ text.scan(/<script[^>]+src\s*=\s*['"]([^>?'"]+\.js)/im).flatten
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,28 @@
1
+ require "#{File.dirname(__FILE__)}/abstract_note"
2
+
3
+ module Footnotes
4
+ module Notes
5
+ class LayoutNote < AbstractNote
6
+ def initialize(controller)
7
+ @controller = controller
8
+ end
9
+
10
+ def row
11
+ :edit
12
+ end
13
+
14
+ def link
15
+ escape(Footnotes::Filter.prefix(filename, 1, 1))
16
+ end
17
+
18
+ def valid?
19
+ prefix? && @controller.active_layout
20
+ end
21
+
22
+ protected
23
+ def filename
24
+ File.join(File.expand_path(RAILS_ROOT), 'app', 'layouts', @controller.active_layout.to_s.underscore).sub('/layouts/layouts/', '/views/layouts/')
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,36 @@
1
+ require "#{File.dirname(__FILE__)}/abstract_note"
2
+
3
+ module Footnotes
4
+ module Notes
5
+ class LogNote < AbstractNote
6
+ def initialize(controller)
7
+ @controller = controller
8
+ end
9
+
10
+ def content
11
+ escape(log_tail).gsub("\n","<br />")
12
+ end
13
+
14
+ protected
15
+ def log_tail
16
+ filename = if RAILS_DEFAULT_LOGGER.instance_variable_get('@log')
17
+ RAILS_DEFAULT_LOGGER.instance_variable_get('@log').path
18
+ else
19
+ RAILS_DEFAULT_LOGGER.instance_variable_get('@logdev').filename
20
+ end
21
+ file_string = File.open(filename).read.to_s
22
+
23
+ # We try to select the specified action from the log
24
+ # If we can't find it, we get the last 100 lines
25
+ #
26
+ if rindex = file_string.rindex('Processing '+@controller.controller_class_name+'#'+@controller.action_name)
27
+ file_string[rindex..-1].gsub(/\e\[.+?m/, '')
28
+ else
29
+ lines = file_string.split("\n")
30
+ index = [lines.size-100,0].max
31
+ lines[index..-1].join("\n")
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,19 @@
1
+ require "#{File.dirname(__FILE__)}/abstract_note"
2
+
3
+ module Footnotes
4
+ module Notes
5
+ class ParamsNote < AbstractNote
6
+ def initialize(controller)
7
+ @params = controller.params.symbolize_keys
8
+ end
9
+
10
+ def title
11
+ "Params (#{@params.length})"
12
+ end
13
+
14
+ def content
15
+ escape(@params.inspect)
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,146 @@
1
+ require "#{File.dirname(__FILE__)}/abstract_note"
2
+
3
+ module Footnotes
4
+ module Notes
5
+ class QueriesNote < AbstractNote
6
+ @@sql = []
7
+ cattr_accessor :sql
8
+
9
+ def self.start!(controller)
10
+ @@sql = []
11
+ end
12
+
13
+ def self.to_sym
14
+ :queries
15
+ end
16
+
17
+ def title
18
+ "Queries (#{@@sql.length})"
19
+ end
20
+
21
+ def stylesheet
22
+ <<-STYLESHEET
23
+ #queries_debug_info table td, #queries_debug_info table th{border:1px solid #A00; padding:0 3px; text-align:center;}
24
+ #queries_debug_info table thead, #queries_debug_info table tbody {color:#A00;}
25
+ #queries_debug_info p {background-color:#F3F3FF; border:1px solid #CCC; margin:12px; padding:4px 6px;}
26
+ #queries_debug_info a:hover {text-decoration:underline;}
27
+ STYLESHEET
28
+ end
29
+
30
+ def content
31
+ html = ''
32
+
33
+ @@sql.each_with_index do |item, i|
34
+ sql_links = []
35
+ sql_links << "<a href=\"#\" style=\"color:#A00;\" onclick=\"Footnotes.toggle('qtable_#{i}');return false\">explain</a>" if item.explain
36
+ sql_links << "<a href=\"#\" style=\"color:#00A;\" onclick=\"Footnotes.toggle('qtrace_#{i}');return false\">trace</a>" if item.trace
37
+
38
+ html << <<-HTML
39
+ <b id="qtitle_#{i}">#{escape(item.type.to_s.upcase)}</b> (#{sql_links.join(' | ')})<br />
40
+ #{print_name_and_time(item.name, item.time)}<br />
41
+ #{print_query(item.query)}<br />
42
+ #{print_explain(i, item.explain) if item.explain}
43
+ <p id="qtrace_#{i}" style="display:none;">#{parse_trace(item.trace) if item.trace}</p><br />
44
+ HTML
45
+ end
46
+
47
+ return html
48
+ end
49
+
50
+ protected
51
+ def parse_explain(results)
52
+ table = []
53
+ table << results.fetch_fields.map(&:name)
54
+ results.each{|row| table << row}
55
+ table
56
+ end
57
+
58
+ def parse_trace(trace)
59
+ trace.map do |t|
60
+ s = t.split(':')
61
+ %[<a href="#{escape(Footnotes::Filter.prefix("#{RAILS_ROOT}/#{s[0]}", s[1].to_i, 1))}">#{escape(t)}</a><br />]
62
+ end.join
63
+ end
64
+
65
+ def print_name_and_time(name, time)
66
+ "#{escape(name || 'SQL')} (#{sprintf('%f', time)}s)"
67
+ end
68
+
69
+ def print_query(query)
70
+ escape(query.to_s.gsub(/(\s)+/, ' ').gsub('`', ''))
71
+ end
72
+
73
+ def print_explain(i, explain)
74
+ mount_table(parse_explain(explain), :id => "qtable_#{i}", :style => 'margin:10px;display:none;')
75
+ end
76
+ end
77
+ end
78
+
79
+ module Extensions
80
+ class Sql
81
+ attr_accessor :type, :name, :time, :query, :explain, :trace
82
+
83
+ def initialize(type, name, time, query, explain)
84
+ @type = type
85
+ @name = name
86
+ @time = time
87
+ @query = query
88
+ @explain = explain
89
+
90
+ # Strip, select those ones from app and reject first two, because they are from the plugin
91
+ @trace = Kernel.caller.collect(&:strip).select{|i| i.gsub!(/^#{RAILS_ROOT}\//im, '') }[2..-1]
92
+ end
93
+ end
94
+
95
+ module QueryAnalyzer
96
+ def self.included(base)
97
+ base.class_eval do
98
+ alias_method_chain :execute, :analyzer
99
+ end
100
+ end
101
+
102
+ def execute_with_analyzer(query, name = nil)
103
+ query_results = nil
104
+ time = Benchmark.realtime { query_results = execute_without_analyzer(query, name) }
105
+
106
+ if query =~ /^(select|create|update|delete)\b/i
107
+ type = $&.downcase.to_sym
108
+ explain = nil
109
+
110
+ if adapter_name == 'MySQL' && type == :select
111
+ log_silence do
112
+ explain = execute_without_analyzer("EXPLAIN #{query}", name)
113
+ end
114
+ end
115
+ Footnotes::Notes::QueriesNote.sql << Footnotes::Extensions::Sql.new(type, name, time, query, explain)
116
+ end
117
+
118
+ query_results
119
+ end
120
+ end
121
+
122
+ module AbstractAdapter
123
+ def log_silence
124
+ result = nil
125
+ if @logger
126
+ @logger.silence do
127
+ result = yield
128
+ end
129
+ else
130
+ result = yield
131
+ end
132
+ result
133
+ end
134
+ end
135
+
136
+ end
137
+ end
138
+
139
+ if Footnotes::Notes::QueriesNote.included?
140
+ ActiveRecord::ConnectionAdapters::AbstractAdapter.send :include, Footnotes::Extensions::AbstractAdapter
141
+ ActiveRecord::ConnectionAdapters.local_constants.each do |adapter|
142
+ next unless adapter =~ /.*[^Abstract]Adapter$/
143
+ next if adapter =~ /SQLiteAdapter$/
144
+ eval("ActiveRecord::ConnectionAdapters::#{adapter}").send :include, Footnotes::Extensions::QueryAnalyzer
145
+ end
146
+ end
@@ -0,0 +1,59 @@
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]))
17
+ end
18
+
19
+ protected
20
+ def parse_routes
21
+ routes_with_name = ActionController::Routing::Routes.named_routes.to_a.flatten
22
+
23
+ return ActionController::Routing::Routes.filtered_routes(:controller => @controller.controller_name).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
+ route.segments.each do |segment|
31
+ next unless segment.is_a?(ActionController::Routing::DynamicSegment) && segment.regexp
32
+ req[segment.key.to_sym] = segment.regexp
33
+ end
34
+
35
+ [escape(name), route.segments.join, route.requirements.reject{|key,value| key == :controller}.inspect, req.inspect]
36
+ end
37
+ end
38
+ end
39
+ end
40
+
41
+ module Extensions
42
+ module Routes
43
+ # Filter routes according to the filter sent
44
+ #
45
+ def filtered_routes(filter = {})
46
+ return [] unless filter.is_a?(Hash)
47
+ return routes.reject do |r|
48
+ filter_diff = filter.diff(r.requirements)
49
+ route_diff = r.requirements.diff(filter)
50
+ (filter_diff == filter) || (filter_diff != route_diff)
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
56
+
57
+ if Footnotes::Notes::RoutesNote.included?
58
+ ActionController::Routing::RouteSet.send :include, Footnotes::Extensions::Routes
59
+ end
@@ -0,0 +1,15 @@
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 || {}).symbolize_keys
8
+ end
9
+
10
+ def content
11
+ escape(@session.inspect)
12
+ end
13
+ end
14
+ end
15
+ 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,46 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = "rails-footnotes"
3
+ s.version = "3.4.1"
4
+ s.date = "2009-02-22"
5
+ s.summary = "Every Rails page has footnotes that gives information about your application and links back to your editor."
6
+ s.email = "jose.valim@gmail.com"
7
+ s.homepage = "http://github.com/josevalim/rails-footnotes"
8
+ s.description = "Every Rails page has footnotes that gives information about your application and links back to your editor."
9
+ s.has_rdoc = true
10
+ s.authors = [ "José Valim" ]
11
+ s.files = [
12
+ "MIT-LICENSE",
13
+ "README",
14
+ "Rakefile",
15
+ "lib/rails-footnotes.rb",
16
+ "lib/rails-footnotes/backtracer.rb",
17
+ "lib/rails-footnotes/footnotes.rb",
18
+ "lib/rails-footnotes/notes/abstract_note.rb",
19
+ "lib/rails-footnotes/notes/components_note.rb",
20
+ "lib/rails-footnotes/notes/controller_note.rb",
21
+ "lib/rails-footnotes/notes/cookies_note.rb",
22
+ "lib/rails-footnotes/notes/env_note.rb",
23
+ "lib/rails-footnotes/notes/files_note.rb",
24
+ "lib/rails-footnotes/notes/filters_note.rb",
25
+ "lib/rails-footnotes/notes/general_note.rb",
26
+ "lib/rails-footnotes/notes/javascripts_note.rb",
27
+ "lib/rails-footnotes/notes/layout_note.rb",
28
+ "lib/rails-footnotes/notes/log_note.rb",
29
+ "lib/rails-footnotes/notes/params_note.rb",
30
+ "lib/rails-footnotes/notes/queries_note.rb",
31
+ "lib/rails-footnotes/notes/routes_note.rb",
32
+ "lib/rails-footnotes/notes/session_note.rb",
33
+ "lib/rails-footnotes/notes/stylesheets_note.rb",
34
+ "lib/rails-footnotes/notes/view_note.rb",
35
+ "test/footnotes_test.rb",
36
+ "test/test_helper.rb",
37
+ "test/notes/abstract_note_test.rb"
38
+ ]
39
+ s.test_files = [
40
+ "test/footnotes_test.rb",
41
+ "test/test_helper.rb",
42
+ "test/notes/abstract_note_test.rb"
43
+ ]
44
+ s.rdoc_options = ["--main", "README"]
45
+ s.extra_rdoc_files = ["README"]
46
+ end
@@ -0,0 +1,199 @@
1
+ require File.dirname(__FILE__) + '/test_helper'
2
+
3
+ require 'action_controller'
4
+ require 'action_controller/test_case'
5
+ require 'action_controller/test_process'
6
+
7
+ class FootnotesController < ActionController::Base; attr_accessor :template, :performed_render; end
8
+
9
+ module Footnotes::Notes
10
+ class TestNote < AbstractNote
11
+ def self.to_sym; :test; end
12
+ def valid?; true; end
13
+ end
14
+ end
15
+
16
+ class FootnotesTest < Test::Unit::TestCase
17
+ def setup
18
+ @controller = FootnotesController.new
19
+ @controller.request = ActionController::TestRequest.new
20
+ @controller.response = ActionController::TestResponse.new
21
+ @controller.response.body = $html.dup
22
+
23
+ Footnotes::Filter.notes = [ :test ]
24
+ Footnotes::Filter.multiple_notes = false
25
+ @footnotes = Footnotes::Filter.new(@controller)
26
+ end
27
+
28
+ def test_footnotes_controller
29
+ index = @controller.response.body.index(/This is the HTML page/)
30
+ assert_equal 334, index
31
+ end
32
+
33
+ def test_foonotes_included
34
+ footnotes_perform!
35
+ assert_not_equal $html, @controller.response.body
36
+ end
37
+
38
+ def test_footnotes_not_included_when_request_is_xhr
39
+ @controller.request.env['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest'
40
+ @controller.request.env['HTTP_ACCEPT'] = 'text/javascript, text/html, application/xml, text/xml, */*'
41
+
42
+ footnotes_perform!
43
+ assert_equal $html, @controller.response.body
44
+ end
45
+
46
+ def test_footnotes_not_included_when_content_type_is_javascript
47
+ @controller.response.headers['Content-Type'] = 'text/javascript'
48
+
49
+ footnotes_perform!
50
+ assert_equal $html, @controller.response.body
51
+ end
52
+
53
+ def test_footnotes_included_when_content_type_is_html
54
+ @controller.response.headers['Content-Type'] = 'text/html'
55
+
56
+ footnotes_perform!
57
+ assert_not_equal $html, @controller.response.body
58
+ end
59
+
60
+ def test_footnotes_included_when_content_type_is_nil
61
+ footnotes_perform!
62
+ assert_not_equal $html, @controller.response.body
63
+ end
64
+
65
+ def test_not_included_when_body_is_not_a_string
66
+ @controller.response.body = Proc.new{ Time.now }
67
+ assert_nothing_raised do
68
+ footnotes_perform!
69
+ end
70
+ end
71
+
72
+ def test_footnotes_prefix
73
+ assert_equal 'txmt://open?url=file://%s&line=%d&column=%d', Footnotes::Filter.prefix
74
+ assert_equal 'txmt://open?url=file://file&line=0&column=0', Footnotes::Filter.prefix('file', 0, 0)
75
+ assert_equal 'txmt://open?url=file://file&line=10&column=10', Footnotes::Filter.prefix('file', 10, 10)
76
+ assert_equal 'txmt://open?url=file://file&line=10&column=10', Footnotes::Filter.prefix('file', 10, 10, 10)
77
+ assert_equal 'txmt://open?url=file://file&line=10&column=10', Footnotes::Filter.prefix('file', '10', '10')
78
+ end
79
+
80
+ def test_notes_are_initialized
81
+ footnotes_perform!
82
+ test_note = @footnotes.instance_variable_get('@notes').first
83
+ assert 'Footnotes::Notes::TestNote', test_note.class
84
+ assert :test, test_note.to_sym
85
+ end
86
+
87
+ def test_notes_links
88
+ note = Footnotes::Notes::TestNote.new
89
+ note.expects(:row).times(2)
90
+ @footnotes.instance_variable_set(:@notes, [note])
91
+ footnotes_perform!
92
+ end
93
+
94
+ def test_notes_fieldset
95
+ note = Footnotes::Notes::TestNote.new
96
+ note.expects(:has_fieldset?).times(3)
97
+ @footnotes.instance_variable_set(:@notes, [note])
98
+ footnotes_perform!
99
+ end
100
+
101
+ def test_multiple_notes
102
+ Footnotes::Filter.multiple_notes = true
103
+ note = Footnotes::Notes::TestNote.new
104
+ note.expects(:has_fieldset?).times(2)
105
+ @footnotes.instance_variable_set(:@notes, [note])
106
+ footnotes_perform!
107
+ end
108
+
109
+ def test_notes_are_reset
110
+ note = Footnotes::Notes::TestNote.new
111
+ note.class.expects(:close!)
112
+ @footnotes.instance_variable_set(:@notes, [note])
113
+ @footnotes.send(:close!, @controller)
114
+ end
115
+
116
+ def test_links_helper
117
+ note = Footnotes::Notes::TestNote.new
118
+ assert_equal '<a href="#" onclick="">Test</a>', @footnotes.send(:link_helper, note)
119
+
120
+ note.expects(:link).times(1).returns(:link)
121
+ assert_equal '<a href="link" onclick="">Test</a>', @footnotes.send(:link_helper, note)
122
+ end
123
+
124
+ def test_links_helper_has_fieldset?
125
+ note = Footnotes::Notes::TestNote.new
126
+ note.expects(:has_fieldset?).times(1).returns(true)
127
+ assert_equal '<a href="#" onclick="Footnotes.hideAllAndToggle(\'test_debug_info\');return false;">Test</a>', @footnotes.send(:link_helper, note)
128
+ end
129
+
130
+ def test_links_helper_onclick
131
+ note = Footnotes::Notes::TestNote.new
132
+ note.expects(:onclick).times(2).returns(:onclick)
133
+ assert_equal '<a href="#" onclick="onclick">Test</a>', @footnotes.send(:link_helper, note)
134
+
135
+ note.expects(:has_fieldset?).times(1).returns(true)
136
+ assert_equal '<a href="#" onclick="onclick">Test</a>', @footnotes.send(:link_helper, note)
137
+ end
138
+
139
+ def test_insert_style
140
+ @controller.response.body = "<head></head><split><body></body>"
141
+ @footnotes = Footnotes::Filter.new(@controller)
142
+ footnotes_perform!
143
+ assert @controller.response.body.split('<split>').first.include?('<!-- Footnotes Style -->')
144
+ end
145
+
146
+ def test_insert_footnotes_inside_body
147
+ @controller.response.body = "<head></head><split><body></body>"
148
+ @footnotes = Footnotes::Filter.new(@controller)
149
+ footnotes_perform!
150
+ assert @controller.response.body.split('<split>').last.include?('<!-- End Footnotes -->')
151
+ end
152
+
153
+ def test_insert_footnotes_inside_holder
154
+ @controller.response.body = "<head></head><split><div id='footnotes_holder'></div>"
155
+ @footnotes = Footnotes::Filter.new(@controller)
156
+ footnotes_perform!
157
+ assert @controller.response.body.split('<split>').last.include?('<!-- End Footnotes -->')
158
+ end
159
+
160
+ def test_insert_text
161
+ @footnotes.send(:insert_text, :after, /<head>/, "Graffiti")
162
+ after = " <head>Graffiti\n"
163
+ assert_equal after, @controller.response.body.to_a[2]
164
+
165
+ @footnotes.send(:insert_text, :before, /<\/body>/, "Notes")
166
+ after = " Notes</body>\n"
167
+ assert_equal after, @controller.response.body.to_a[12]
168
+ end
169
+
170
+ protected
171
+ # First we make sure that footnotes will perform (long life to mocha!)
172
+ # Then we call add_footnotes!
173
+ #
174
+ def footnotes_perform!
175
+ @controller.template.expects(:instance_variable_get).returns(true)
176
+ @controller.template.expects(:template_format).returns('html')
177
+ @controller.performed_render = true
178
+
179
+ Footnotes::Filter.start!(@controller)
180
+ @footnotes.add_footnotes!
181
+ end
182
+ end
183
+
184
+ $html = <<HTML
185
+ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
186
+ <html>
187
+ <head>
188
+ <title>HTML to XHTML Example: HTML page</title>
189
+ <link rel="Stylesheet" href="htmltohxhtml.css" type="text/css" media="screen">
190
+ <meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
191
+ </head>
192
+ <body>
193
+ <p>This is the HTML page. It works and is encoded just like any HTML page you
194
+ have previously done. View <a href="htmltoxhtml2.htm">the XHTML version</a> of
195
+ this page to view the difference between HTML and XHTML.</p>
196
+ <p>You will be glad to know that no changes need to be made to any of your CSS files.</p>
197
+ </body>
198
+ </html>
199
+ HTML