rails6-footnotes 5.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (68) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/ci.yml +25 -0
  3. data/.gitignore +15 -0
  4. data/.rspec.example +1 -0
  5. data/CHANGELOG +129 -0
  6. data/Gemfile +10 -0
  7. data/Gemfile.lock +187 -0
  8. data/MIT-LICENSE +21 -0
  9. data/README.rdoc +188 -0
  10. data/Rakefile +18 -0
  11. data/bin/rake +29 -0
  12. data/bin/rspec +29 -0
  13. data/gemfiles/Gemfile.rails-3.2.22 +5 -0
  14. data/gemfiles/Gemfile.rails-4.0.x +6 -0
  15. data/gemfiles/Gemfile.rails-4.1.x +5 -0
  16. data/gemfiles/Gemfile.rails-4.2.x +5 -0
  17. data/gemfiles/Gemfile.rails-edge +5 -0
  18. data/lib/generators/rails_footnotes/install_generator.rb +14 -0
  19. data/lib/generators/templates/rails_footnotes.rb +26 -0
  20. data/lib/rails-footnotes.rb +68 -0
  21. data/lib/rails-footnotes/abstract_note.rb +178 -0
  22. data/lib/rails-footnotes/each_with_rescue.rb +36 -0
  23. data/lib/rails-footnotes/extension.rb +24 -0
  24. data/lib/rails-footnotes/filter.rb +359 -0
  25. data/lib/rails-footnotes/notes/all.rb +1 -0
  26. data/lib/rails-footnotes/notes/assigns_note.rb +60 -0
  27. data/lib/rails-footnotes/notes/controller_note.rb +55 -0
  28. data/lib/rails-footnotes/notes/cookies_note.rb +17 -0
  29. data/lib/rails-footnotes/notes/env_note.rb +24 -0
  30. data/lib/rails-footnotes/notes/files_note.rb +49 -0
  31. data/lib/rails-footnotes/notes/filters_note.rb +51 -0
  32. data/lib/rails-footnotes/notes/javascripts_note.rb +16 -0
  33. data/lib/rails-footnotes/notes/layout_note.rb +26 -0
  34. data/lib/rails-footnotes/notes/log_note.rb +47 -0
  35. data/lib/rails-footnotes/notes/log_note/note_logger.rb +59 -0
  36. data/lib/rails-footnotes/notes/params_note.rb +21 -0
  37. data/lib/rails-footnotes/notes/partials_note.rb +38 -0
  38. data/lib/rails-footnotes/notes/queries_note.rb +121 -0
  39. data/lib/rails-footnotes/notes/routes_note.rb +60 -0
  40. data/lib/rails-footnotes/notes/session_note.rb +27 -0
  41. data/lib/rails-footnotes/notes/stylesheets_note.rb +16 -0
  42. data/lib/rails-footnotes/notes/view_note.rb +41 -0
  43. data/lib/rails-footnotes/version.rb +3 -0
  44. data/lib/rails6-footnotes.rb +1 -0
  45. data/rails-footnotes.gemspec +22 -0
  46. data/spec/abstract_note_spec.rb +89 -0
  47. data/spec/app/assets/config/manifest.js +2 -0
  48. data/spec/app/assets/javascripts/foobar.js +1 -0
  49. data/spec/app/assets/stylesheets/foobar.css +0 -0
  50. data/spec/app/views/files/index.html.erb +1 -0
  51. data/spec/app/views/layouts/application.html.erb +12 -0
  52. data/spec/app/views/partials/_foo.html.erb +1 -0
  53. data/spec/app/views/partials/index.html.erb +1 -0
  54. data/spec/controllers/files_note_controller_spec.rb +38 -0
  55. data/spec/controllers/footnotes_controller_spec.rb +128 -0
  56. data/spec/controllers/log_note_controller_spec.rb +32 -0
  57. data/spec/controllers/partials_note_controller_spec.rb +28 -0
  58. data/spec/env_note_spec.rb +73 -0
  59. data/spec/fixtures/html_download.html +5 -0
  60. data/spec/footnotes_spec.rb +234 -0
  61. data/spec/notes/assigns_note_spec.rb +50 -0
  62. data/spec/notes/controller_note_spec.rb +12 -0
  63. data/spec/notes/files_note_spec.rb +26 -0
  64. data/spec/notes/javascripts_note_spec.rb +18 -0
  65. data/spec/notes/stylesheets_note_spec.rb +19 -0
  66. data/spec/notes/view_note_spec.rb +12 -0
  67. data/spec/spec_helper.rb +68 -0
  68. 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,3 @@
1
+ module Footnotes
2
+ VERSION = "5.0.0"
3
+ 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&amp;line=%d&amp;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 '&lt;' }
53
+ specify { expect(subject.escape('&')).to eql '&amp;' }
54
+ specify { expect(subject.escape('>')).to eql '&gt;' }
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,2 @@
1
+ //= link_directory ../javascripts .js
2
+ //= link_directory ../stylesheets .css
@@ -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