rails6-footnotes 5.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.
- checksums.yaml +7 -0
- data/.github/workflows/ci.yml +25 -0
- data/.gitignore +15 -0
- data/.rspec.example +1 -0
- data/CHANGELOG +129 -0
- data/Gemfile +10 -0
- data/Gemfile.lock +187 -0
- data/MIT-LICENSE +21 -0
- data/README.rdoc +188 -0
- data/Rakefile +18 -0
- data/bin/rake +29 -0
- data/bin/rspec +29 -0
- data/gemfiles/Gemfile.rails-3.2.22 +5 -0
- data/gemfiles/Gemfile.rails-4.0.x +6 -0
- data/gemfiles/Gemfile.rails-4.1.x +5 -0
- data/gemfiles/Gemfile.rails-4.2.x +5 -0
- data/gemfiles/Gemfile.rails-edge +5 -0
- data/lib/generators/rails_footnotes/install_generator.rb +14 -0
- data/lib/generators/templates/rails_footnotes.rb +26 -0
- data/lib/rails-footnotes.rb +68 -0
- data/lib/rails-footnotes/abstract_note.rb +178 -0
- data/lib/rails-footnotes/each_with_rescue.rb +36 -0
- data/lib/rails-footnotes/extension.rb +24 -0
- data/lib/rails-footnotes/filter.rb +359 -0
- data/lib/rails-footnotes/notes/all.rb +1 -0
- data/lib/rails-footnotes/notes/assigns_note.rb +60 -0
- data/lib/rails-footnotes/notes/controller_note.rb +55 -0
- data/lib/rails-footnotes/notes/cookies_note.rb +17 -0
- data/lib/rails-footnotes/notes/env_note.rb +24 -0
- data/lib/rails-footnotes/notes/files_note.rb +49 -0
- data/lib/rails-footnotes/notes/filters_note.rb +51 -0
- data/lib/rails-footnotes/notes/javascripts_note.rb +16 -0
- data/lib/rails-footnotes/notes/layout_note.rb +26 -0
- data/lib/rails-footnotes/notes/log_note.rb +47 -0
- data/lib/rails-footnotes/notes/log_note/note_logger.rb +59 -0
- data/lib/rails-footnotes/notes/params_note.rb +21 -0
- data/lib/rails-footnotes/notes/partials_note.rb +38 -0
- data/lib/rails-footnotes/notes/queries_note.rb +121 -0
- data/lib/rails-footnotes/notes/routes_note.rb +60 -0
- data/lib/rails-footnotes/notes/session_note.rb +27 -0
- data/lib/rails-footnotes/notes/stylesheets_note.rb +16 -0
- data/lib/rails-footnotes/notes/view_note.rb +41 -0
- data/lib/rails-footnotes/version.rb +3 -0
- data/lib/rails6-footnotes.rb +1 -0
- data/rails-footnotes.gemspec +22 -0
- data/spec/abstract_note_spec.rb +89 -0
- data/spec/app/assets/config/manifest.js +2 -0
- data/spec/app/assets/javascripts/foobar.js +1 -0
- data/spec/app/assets/stylesheets/foobar.css +0 -0
- data/spec/app/views/files/index.html.erb +1 -0
- data/spec/app/views/layouts/application.html.erb +12 -0
- data/spec/app/views/partials/_foo.html.erb +1 -0
- data/spec/app/views/partials/index.html.erb +1 -0
- data/spec/controllers/files_note_controller_spec.rb +38 -0
- data/spec/controllers/footnotes_controller_spec.rb +128 -0
- data/spec/controllers/log_note_controller_spec.rb +32 -0
- data/spec/controllers/partials_note_controller_spec.rb +28 -0
- data/spec/env_note_spec.rb +73 -0
- data/spec/fixtures/html_download.html +5 -0
- data/spec/footnotes_spec.rb +234 -0
- data/spec/notes/assigns_note_spec.rb +50 -0
- data/spec/notes/controller_note_spec.rb +12 -0
- data/spec/notes/files_note_spec.rb +26 -0
- data/spec/notes/javascripts_note_spec.rb +18 -0
- data/spec/notes/stylesheets_note_spec.rb +19 -0
- data/spec/notes/view_note_spec.rb +12 -0
- data/spec/spec_helper.rb +68 -0
- metadata +153 -0
@@ -0,0 +1,60 @@
|
|
1
|
+
module Footnotes
|
2
|
+
module Notes
|
3
|
+
class RoutesNote < AbstractNote
|
4
|
+
def initialize(controller)
|
5
|
+
@controller = controller
|
6
|
+
@parsed_routes = parse_routes
|
7
|
+
end
|
8
|
+
|
9
|
+
def legend
|
10
|
+
"Routes for #{@controller.class.to_s}"
|
11
|
+
end
|
12
|
+
|
13
|
+
def content
|
14
|
+
mount_table(@parsed_routes.unshift([:path, :name, :options, :requirements]), :summary => "Debug information for #{title}")
|
15
|
+
end
|
16
|
+
|
17
|
+
protected
|
18
|
+
def parse_routes
|
19
|
+
routes_with_name = Rails.application.routes.named_routes.to_a.flatten
|
20
|
+
|
21
|
+
return Rails.application.routes.filtered_routes(:controller => @controller.controller_path).collect do |route|
|
22
|
+
# Catch routes name if exists
|
23
|
+
i = routes_with_name.index(route)
|
24
|
+
name = i ? routes_with_name[i-1].to_s : ''
|
25
|
+
|
26
|
+
# Catch segments requirements
|
27
|
+
req = route.conditions
|
28
|
+
[escape(name), route.conditions.keys.join, route.requirements.reject{|key,value| key == :controller}.inspect, req.inspect]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
module Extensions
|
35
|
+
module Routes
|
36
|
+
|
37
|
+
def __hash_diff(hash_self, other)
|
38
|
+
# ActiveSupport::Deprecation.warn "Hash#diff is no longer used inside of Rails,
|
39
|
+
# and is being deprecated with no replacement. If you're using it to compare hashes for the purpose of testing, please use MiniTest's diff instead."
|
40
|
+
hash_self.dup.delete_if { |k, v| other[k] == v }.merge!(other.dup.delete_if { |k, v| hash_self.has_key?(k) })
|
41
|
+
end
|
42
|
+
|
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 = __hash_diff(filter, r.requirements)
|
49
|
+
route_diff = __hash_diff(r.requirements, filter)
|
50
|
+
|
51
|
+
(filter_diff == filter) || (filter_diff != route_diff)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
if Footnotes::Notes::RoutesNote.included?
|
59
|
+
ActionDispatch::Routing::RouteSet.send :include, Footnotes::Extensions::Routes
|
60
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Footnotes
|
2
|
+
module Notes
|
3
|
+
class SessionNote < AbstractNote
|
4
|
+
def initialize(controller)
|
5
|
+
session = controller.session
|
6
|
+
if session
|
7
|
+
if session.respond_to? :to_hash
|
8
|
+
# rails >= 2.3
|
9
|
+
session = session.to_hash
|
10
|
+
else
|
11
|
+
#rails < 2.3
|
12
|
+
session = session.data
|
13
|
+
end
|
14
|
+
end
|
15
|
+
@session = (session || {}).symbolize_keys
|
16
|
+
end
|
17
|
+
|
18
|
+
def title
|
19
|
+
"Session (#{@session.length})"
|
20
|
+
end
|
21
|
+
|
22
|
+
def content
|
23
|
+
mount_table_for_hash(@session, :summary => "Debug information for #{title}")
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require "rails-footnotes/notes/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,41 @@
|
|
1
|
+
module Footnotes
|
2
|
+
module Notes
|
3
|
+
class ViewNote < AbstractNote
|
4
|
+
cattr_accessor :template
|
5
|
+
|
6
|
+
def self.start!(controller)
|
7
|
+
@subscriber ||= ActiveSupport::Notifications.subscribe('render_template.action_view') do |*args|
|
8
|
+
event = ActiveSupport::Notifications::Event.new *args
|
9
|
+
self.template = {:file => event.payload[:identifier], :duration => event.duration}
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize(controller)
|
14
|
+
@controller = controller
|
15
|
+
end
|
16
|
+
|
17
|
+
def row
|
18
|
+
:edit
|
19
|
+
end
|
20
|
+
|
21
|
+
def title
|
22
|
+
"View (#{"%.3f" % self.template[:duration]}ms)"
|
23
|
+
end
|
24
|
+
|
25
|
+
def link
|
26
|
+
escape(Footnotes::Filter.prefix(filename, 1, 1))
|
27
|
+
end
|
28
|
+
|
29
|
+
def valid?
|
30
|
+
prefix? && filename && File.exists?(filename)
|
31
|
+
end
|
32
|
+
|
33
|
+
protected
|
34
|
+
|
35
|
+
def filename
|
36
|
+
@filename ||= self.class.template[:file]
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require_relative "./rails-footnotes"
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "rails-footnotes/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "rails6-footnotes"
|
7
|
+
s.version = Footnotes::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Roman V. Babenko", "José Valim", "Keenan Brock", "Duane Johnson", "Adrien Siami", "André Arko"]
|
10
|
+
s.email = ["andre@arko.net"]
|
11
|
+
s.homepage = "http://github.com/indirect/rails-footnotes"
|
12
|
+
s.summary = %q{Every Rails page has footnotes that gives information about your application and links back to your editor.}
|
13
|
+
s.description = %q{Every Rails page has footnotes that gives information about your application and links back to your editor.}
|
14
|
+
|
15
|
+
s.add_dependency "rails", "~> 6.0"
|
16
|
+
s.required_ruby_version = "~> 3.0"
|
17
|
+
|
18
|
+
s.files = `git ls-files`.split("\n")
|
19
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
20
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
|
21
|
+
s.require_paths = ["lib"]
|
22
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Footnotes::Notes::AbstractNote do
|
4
|
+
before do
|
5
|
+
@note = Footnotes::Notes::AbstractNote.new
|
6
|
+
@notes = Footnotes::Filter.notes
|
7
|
+
Footnotes::Filter.notes = [:abstract]
|
8
|
+
end
|
9
|
+
|
10
|
+
after do
|
11
|
+
Footnotes::Filter.notes = @notes
|
12
|
+
end
|
13
|
+
|
14
|
+
it {expect(described_class).to respond_to :start!}
|
15
|
+
it {expect(described_class).to respond_to :close!}
|
16
|
+
it {expect(described_class).to respond_to :title}
|
17
|
+
|
18
|
+
it {should respond_to :to_sym}
|
19
|
+
|
20
|
+
describe '#to_sym' do
|
21
|
+
subject { super().to_sym }
|
22
|
+
it {should eql :abstract}
|
23
|
+
end
|
24
|
+
|
25
|
+
it { expect(described_class).to be_included }
|
26
|
+
specify do
|
27
|
+
Footnotes::Filter.notes = []
|
28
|
+
expect(described_class).not_to be_included
|
29
|
+
end
|
30
|
+
|
31
|
+
it { should respond_to :row }
|
32
|
+
it { should respond_to :legend }
|
33
|
+
it { should respond_to :link }
|
34
|
+
it { should respond_to :onclick }
|
35
|
+
it { should respond_to :stylesheet }
|
36
|
+
it { should respond_to :javascript }
|
37
|
+
|
38
|
+
it { should respond_to :valid? }
|
39
|
+
it { should be_valid }
|
40
|
+
|
41
|
+
it { should respond_to :has_fieldset? }
|
42
|
+
it { should_not have_fieldset }
|
43
|
+
|
44
|
+
specify { Footnotes::Filter.prefix = ''; should_not be_prefix }
|
45
|
+
specify do
|
46
|
+
Footnotes::Filter.prefix = 'txmt://open?url=file://%s&line=%d&column=%d'
|
47
|
+
should be_prefix
|
48
|
+
end
|
49
|
+
|
50
|
+
#TODO should be moved to builder
|
51
|
+
#helpers
|
52
|
+
specify { expect(subject.escape('<')).to eql '<' }
|
53
|
+
specify { expect(subject.escape('&')).to eql '&' }
|
54
|
+
specify { expect(subject.escape('>')).to eql '>' }
|
55
|
+
|
56
|
+
specify { expect(subject.mount_table([])).to be_blank }
|
57
|
+
specify { expect(subject.mount_table([['h1', 'h2', 'h3']], :class => 'table')).to be_blank }
|
58
|
+
|
59
|
+
specify {
|
60
|
+
tab = <<-TABLE
|
61
|
+
<table class="table" >
|
62
|
+
<thead><tr><th>H1</th></tr></thead>
|
63
|
+
<tbody><tr><td>r1c1</td></tr></tbody>
|
64
|
+
</table>
|
65
|
+
TABLE
|
66
|
+
|
67
|
+
expect(subject.mount_table([['h1'],['r1c1']], :class => 'table')).to eql tab
|
68
|
+
}
|
69
|
+
|
70
|
+
specify {
|
71
|
+
tab = <<-TABLE
|
72
|
+
<table >
|
73
|
+
<thead><tr><th>H1</th><th>H2</th><th>H3</th></tr></thead>
|
74
|
+
<tbody><tr><td>r1c1</td><td>r1c2</td><td>r1c3</td></tr></tbody>
|
75
|
+
</table>
|
76
|
+
TABLE
|
77
|
+
expect(subject.mount_table([['h1', 'h2', 'h3'],['r1c1', 'r1c2', 'r1c3']])).to eql tab
|
78
|
+
}
|
79
|
+
|
80
|
+
specify {
|
81
|
+
tab = <<-TABLE
|
82
|
+
<table >
|
83
|
+
<thead><tr><th>H1</th><th>H2</th><th>H3</th></tr></thead>
|
84
|
+
<tbody><tr><td>r1c1</td><td>r1c2</td><td>r1c3</td></tr><tr><td>r2c1</td><td>r2c2</td><td>r2c3</td></tr></tbody>
|
85
|
+
</table>
|
86
|
+
TABLE
|
87
|
+
subject.mount_table([['h1', 'h2', 'h3'], ['r1c1', 'r1c2', 'r1c3'], ['r2c1', 'r2c2', 'r2c3']])
|
88
|
+
}
|
89
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
// Foobar
|
File without changes
|
@@ -0,0 +1 @@
|
|
1
|
+
FILES INDEX
|
@@ -0,0 +1,12 @@
|
|
1
|
+
<!doctype html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<%= javascript_include_tag 'foobar' %>
|
5
|
+
<%= javascript_include_tag 'http://google.com/whatever.js' %>
|
6
|
+
<%= stylesheet_link_tag 'foobar' %>
|
7
|
+
<%= stylesheet_link_tag 'http://google.com/whatever.css' %>
|
8
|
+
</head>
|
9
|
+
<body>
|
10
|
+
<%= yield %>
|
11
|
+
</body>
|
12
|
+
</html>
|
@@ -0,0 +1 @@
|
|
1
|
+
bar
|
@@ -0,0 +1 @@
|
|
1
|
+
<%= render 'foo' %>
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class FilesController < ApplicationController
|
4
|
+
def index
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
describe FilesController, type: :controller do
|
9
|
+
render_views
|
10
|
+
|
11
|
+
before :all do
|
12
|
+
Footnotes.enabled = true
|
13
|
+
end
|
14
|
+
|
15
|
+
after :all do
|
16
|
+
Footnotes.enabled = false
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'includes stylesheets assets in the response' do
|
20
|
+
get :index
|
21
|
+
expect(response.body).to include("FILES INDEX")
|
22
|
+
js_debug = first('fieldset#javascripts_debug_info div', visible: false)
|
23
|
+
expect(js_debug).to have_selector('li a', visible: false, count: 1)
|
24
|
+
expect(js_debug).to have_selector('li a', text: /foobar\.js/, visible: false)
|
25
|
+
link = js_debug.first('a', visible: false)
|
26
|
+
expect(link['href']).to eq("txmt://open?url=file://#{Rails.root.join('app', 'assets', 'javascripts', 'foobar.js')}&line=1&column=1")
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'includes css assets in the response' do
|
30
|
+
get :index
|
31
|
+
css_debug = first('fieldset#stylesheets_debug_info div', visible: false)
|
32
|
+
expect(css_debug).to have_selector('li a', visible: false, count: 1)
|
33
|
+
expect(css_debug).to have_selector('li a', text: /foobar\.css/, visible: false)
|
34
|
+
link = css_debug.first('a', visible: false)
|
35
|
+
expect(link['href']).to eq("txmt://open?url=file://#{Rails.root.join('app', 'assets', 'stylesheets', 'foobar.css')}&line=1&column=1")
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
@@ -0,0 +1,128 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class FootnotesController < ActionController::Base
|
4
|
+
|
5
|
+
def foo
|
6
|
+
render inline: HTML_DOCUMENT, content_type: 'text/html'
|
7
|
+
end
|
8
|
+
|
9
|
+
def foo_holder
|
10
|
+
render inline: '<html><body><div id="footnotes_holder"></div></body></html>'
|
11
|
+
end
|
12
|
+
|
13
|
+
def foo_js
|
14
|
+
render inline: '<script></script>', content_type: 'text/javascript'
|
15
|
+
end
|
16
|
+
|
17
|
+
def foo_download
|
18
|
+
send_file Rails.root.join('fixtures', 'html_download.html'), :disposition => 'attachment'
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
describe FootnotesController, type: :controller do
|
24
|
+
|
25
|
+
shared_examples 'has_footnotes' do
|
26
|
+
it 'includes footnotes' do
|
27
|
+
get :foo
|
28
|
+
expect(response.body).to have_selector('#footnotes_debug')
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
shared_examples 'has_no_footnotes' do
|
33
|
+
it 'does not include footnotes' do
|
34
|
+
expect(response.body).not_to have_selector('#footnotes_debug')
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'does not alter the page by default' do
|
39
|
+
get :foo
|
40
|
+
expect(response.body).to eq(HTML_DOCUMENT)
|
41
|
+
end
|
42
|
+
|
43
|
+
context 'with footnotes' do
|
44
|
+
|
45
|
+
before :all do
|
46
|
+
Footnotes.enabled = true
|
47
|
+
end
|
48
|
+
|
49
|
+
after :all do
|
50
|
+
Footnotes.enabled = false
|
51
|
+
end
|
52
|
+
|
53
|
+
describe 'by default' do
|
54
|
+
include_context 'has_footnotes'
|
55
|
+
|
56
|
+
before do
|
57
|
+
get :foo
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'includes footnotes in the last div in body' do
|
61
|
+
expect(all('body > :last-child')[0][:id]).to eq('footnotes_debug')
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'includes footnotes in the footnoted_holder div if present' do
|
65
|
+
get :foo_holder
|
66
|
+
expect(response.body).to have_selector('#footnotes_holder > #footnotes_debug')
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'does not alter a html file download' do
|
70
|
+
get :foo_download
|
71
|
+
expect(response.body).to eq(File.open(Rails.root.join('fixtures', 'html_download.html')).read)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
describe 'when request is xhr' do
|
76
|
+
include_context 'has_no_footnotes'
|
77
|
+
before do
|
78
|
+
get :foo, xhr: true
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
describe 'when content type is javascript' do
|
83
|
+
include_context 'has_no_footnotes'
|
84
|
+
before do
|
85
|
+
get :foo_js
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
describe 'when footnotes is disabled' do
|
90
|
+
include_context 'has_no_footnotes'
|
91
|
+
before do
|
92
|
+
Footnotes.enabled = false
|
93
|
+
get :foo
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
describe 'with a proc' do
|
98
|
+
|
99
|
+
it 'yields the controller' do
|
100
|
+
c = nil
|
101
|
+
Footnotes.enabled = lambda { |controller| c = controller}
|
102
|
+
get :foo
|
103
|
+
expect(c).to be_kind_of(ActionController::Base)
|
104
|
+
end
|
105
|
+
|
106
|
+
context 'returning true' do
|
107
|
+
include_context 'has_footnotes'
|
108
|
+
|
109
|
+
before do
|
110
|
+
Footnotes.enabled = lambda { true }
|
111
|
+
get :foo
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
context 'returning false' do
|
116
|
+
include_context 'has_no_footnotes'
|
117
|
+
|
118
|
+
before do
|
119
|
+
Footnotes.enabled = lambda { false }
|
120
|
+
get :foo
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
end
|
125
|
+
|
126
|
+
end
|
127
|
+
|
128
|
+
end
|