rails-footnotes 3.7.9 → 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. data/.gitignore +4 -3
  2. data/.travis.yml +10 -6
  3. data/CHANGELOG +13 -0
  4. data/README.rdoc +33 -46
  5. data/Rakefile +2 -2
  6. data/gemfiles/Gemfile.rails-3.2.x +5 -0
  7. data/gemfiles/Gemfile.rails-4.0.x +6 -0
  8. data/gemfiles/Gemfile.rails-4.1.x +5 -0
  9. data/gemfiles/Gemfile.rails-edge +5 -0
  10. data/lib/generators/rails_footnotes/install_generator.rb +1 -4
  11. data/lib/generators/templates/rails_footnotes.rb +19 -3
  12. data/lib/rails-footnotes.rb +41 -13
  13. data/lib/rails-footnotes/extension.rb +22 -3
  14. data/lib/rails-footnotes/filter.rb +9 -18
  15. data/lib/rails-footnotes/notes/log_note.rb +18 -25
  16. data/lib/rails-footnotes/notes/log_note/note_logger.rb +59 -0
  17. data/lib/rails-footnotes/notes/partials_note.rb +20 -38
  18. data/lib/rails-footnotes/notes/queries_note.rb +1 -1
  19. data/lib/rails-footnotes/notes/routes_note.rb +13 -14
  20. data/lib/rails-footnotes/version.rb +1 -1
  21. data/rails-footnotes.gemspec +5 -6
  22. data/spec/abstract_note_spec.rb +5 -0
  23. data/spec/controllers/footnotes_controller_spec.rb +113 -0
  24. data/spec/controllers/log_note_controller_spec.rb +45 -0
  25. data/spec/controllers/partials_note_controller_spec.rb +29 -0
  26. data/spec/fixtures/html_download.html +5 -0
  27. data/spec/footnotes_spec.rb +5 -8
  28. data/spec/notes/assigns_note_spec.rb +1 -1
  29. data/spec/notes/controller_note_spec.rb +1 -1
  30. data/spec/notes/files_note_spec.rb +1 -1
  31. data/spec/notes/javascripts_note_spec.rb +1 -1
  32. data/spec/notes/stylesheets_note_spec.rb +1 -1
  33. data/spec/spec_helper.rb +23 -12
  34. data/spec/views/partials/_foo.html.erb +1 -0
  35. data/spec/views/partials/index.html.erb +1 -0
  36. metadata +31 -22
  37. data/.watchr.example +0 -13
  38. data/lib/generators/templates/rails_footnotes +0 -3
  39. data/lib/rails-footnotes/after_filter.rb +0 -10
  40. data/lib/rails-footnotes/backtracer.rb +0 -32
  41. data/lib/rails-footnotes/before_filter.rb +0 -9
  42. data/lib/rails-footnotes/footnotes.rb +0 -7
  43. data/lib/rails-footnotes/notes/general_note.rb +0 -17
  44. data/spec/notes/partials_notes_spec.rb +0 -9
@@ -1,14 +1,21 @@
1
1
  module Footnotes
2
2
  module Notes
3
3
  class LogNote < AbstractNote
4
- @@log = []
5
4
 
6
- def self.log(message)
7
- @@log << message
8
- end
5
+ autoload :NoteLogger, 'rails-footnotes/notes/log_note/note_logger'
6
+
7
+ cattr_accessor :logs
8
+ cattr_accessor :original_logger
9
9
 
10
- def initialize(controller)
11
- @controller = controller
10
+ def self.start!(controller)
11
+ self.logs = []
12
+ self.original_logger = Rails.logger
13
+ note_logger = NoteLogger.new(self.logs)
14
+ note_logger.level = self.original_logger.level
15
+ note_logger.formatter = self.original_logger.kind_of?(Logger) ? self.original_logger.formatter : Logger::SimpleFormatter.new
16
+ # Rails 3 don't have ActiveSupport::Logger#broadcast so we backported it
17
+ extend_module = defined?(ActiveSupport::Logger) ? ActiveSupport::Logger.broadcast(note_logger) : NoteLogger.broadcast(note_logger)
18
+ Rails.logger = self.original_logger.dup.extend(extend_module)
12
19
  end
13
20
 
14
21
  def title
@@ -16,30 +23,16 @@ module Footnotes
16
23
  end
17
24
 
18
25
  def content
19
- escape(log.gsub(/\e\[.+?m/, '')).gsub("\n", '<br />')
26
+ result = escape(log.gsub(/\e\[.+?m/, '')).gsub("\n", '<br />')
27
+ # Restore formatter
28
+ Rails.logger = self.class.original_logger
29
+ result
20
30
  end
21
31
 
22
32
  def log
23
- unless @log
24
- @log = @@log.join('')
25
- @@log = []
26
- if rindex = @log.rindex('Processing '+@controller.class.name+'#'+@controller.action_name)
27
- @log = @log[rindex..-1]
28
- end
29
- end
30
- @log
33
+ self.class.logs.join("\n")
31
34
  end
32
35
 
33
- module LoggingExtensions
34
- def add(*args, &block)
35
- logged_message = super
36
- Footnotes::Notes::LogNote.log(logged_message)
37
- logged_message
38
- end
39
- end
40
-
41
- Rails.logger.extend LoggingExtensions
42
36
  end
43
37
  end
44
38
  end
45
-
@@ -0,0 +1,59 @@
1
+ module Footnotes
2
+ module Notes
3
+ class LogNote
4
+ class NoteLogger < Logger
5
+
6
+ def initialize(logs)
7
+ @logs = logs
8
+ end
9
+
10
+ def add(severity, message = nil, progname = nil, &block)
11
+ severity ||= UNKNOWN
12
+ if severity < level
13
+ return true
14
+ end
15
+ formatter = @formatter || Logger::Formatter.new
16
+ @logs << formatter.call(format_severity(severity), Time.now, message, progname)
17
+ end
18
+
19
+ ## Backport from rails 4 for handling logging broadcast, should be removed when rails 3 is deprecated :
20
+
21
+ # Broadcasts logs to multiple loggers.
22
+ def self.broadcast(logger) # :nodoc:
23
+ Module.new do
24
+ define_method(:add) do |*args, &block|
25
+ logger.add(*args, &block)
26
+ super(*args, &block)
27
+ end
28
+
29
+ define_method(:<<) do |x|
30
+ logger << x
31
+ super(x)
32
+ end
33
+
34
+ define_method(:close) do
35
+ logger.close
36
+ super()
37
+ end
38
+
39
+ define_method(:progname=) do |name|
40
+ logger.progname = name
41
+ super(name)
42
+ end
43
+
44
+ define_method(:formatter=) do |formatter|
45
+ logger.formatter = formatter
46
+ super(formatter)
47
+ end
48
+
49
+ define_method(:level=) do |level|
50
+ logger.level = level
51
+ super(level)
52
+ end
53
+ end
54
+ end
55
+
56
+ end
57
+ end
58
+ end
59
+ end
@@ -1,56 +1,38 @@
1
- require 'rails-footnotes/notes/log_note'
2
-
3
1
  module Footnotes
4
2
  module Notes
5
- class PartialsNote < LogNote
3
+ class PartialsNote < AbstractNote
4
+
5
+ cattr_accessor :partials
6
+
7
+ def self.start!(controller)
8
+ self.partials = []
9
+ @subscriber ||= ActiveSupport::Notifications.subscribe('render_partial.action_view') do |*args|
10
+ event = ActiveSupport::Notifications::Event.new *args
11
+ self.partials << {:file => event.payload[:identifier], :duration => event.duration}
12
+ end
13
+ end
14
+
6
15
  def initialize(controller)
7
- super
8
16
  @controller = controller
9
17
  end
18
+
10
19
  def row
11
20
  :edit
12
21
  end
22
+
13
23
  def title
14
24
  "Partials (#{partials.size})"
15
25
  end
26
+
16
27
  def content
17
- rows = partials.map do |filename|
18
- href = Footnotes::Filter.prefix(filename,1,1)
19
- shortened_name=filename.gsub(File.join(Rails.root,"app/views/"),"")
20
- [%{<a href="#{href}">#{shortened_name}</a>},"#{@partial_times[filename].sum}ms",@partial_counts[filename]]
28
+ rows = self.class.partials.map do |partial|
29
+ href = Footnotes::Filter.prefix(partial[:file],1,1)
30
+ shortened_name = partial[:file].gsub(File.join(Rails.root,"app/views/"),"")
31
+ [%{<a href="#{href}">#{shortened_name}</a>},"#{partial[:duration]}ms"]
21
32
  end
22
- mount_table(rows.unshift(%w(Partial Time Count)), :summary => "Partials for #{title}")
33
+ mount_table(rows.unshift(%w(Partial Time)), :summary => "Partials for #{title}")
23
34
  end
24
35
 
25
- protected
26
- #Generate a list of partials that were rendered, also build up render times and counts.
27
- #This is memoized so we can use its information in the title easily.
28
- def partials
29
- @partials ||= begin
30
- partials = []
31
- @partial_counts = {}
32
- @partial_times = {}
33
- log_lines = log
34
- log_lines.split("\n").each do |line|
35
- if line =~ /Rendered (\S*) \(([\d\.]+)\S*?\)/
36
- partial = $1
37
- @controller.view_paths.each do |view_path|
38
- path = File.join(view_path.to_s, "#{partial}*")
39
- files = Dir.glob(path)
40
- for file in files
41
- #TODO figure out what format got rendered if theres multiple
42
- @partial_times[file] ||= []
43
- @partial_times[file] << $2.to_f
44
- @partial_counts[file] ||= 0
45
- @partial_counts[file] += 1
46
- partials << file unless partials.include?(file)
47
- end
48
- end
49
- end
50
- end
51
- partials.reverse
52
- end
53
- end
54
36
  end
55
37
  end
56
38
  end
@@ -6,7 +6,7 @@ module Footnotes
6
6
  @@alert_sql_number = 8
7
7
  @@query_subscriber = nil
8
8
  @@orm = [:active_record, :data_mapper]
9
- @@ignored_regexps = [%r{(pg_table|pg_attribute|show\stables|pragma|sqlite_master)}i]
9
+ @@ignored_regexps = [%r{(pg_table|pg_attribute|pg_namespace|show\stables|pragma|sqlite_master)}i]
10
10
 
11
11
  def self.start!(controller)
12
12
  self.query_subscriber.reset!
@@ -24,17 +24,8 @@ module Footnotes
24
24
  name = i ? routes_with_name[i-1].to_s : ''
25
25
 
26
26
  # Catch segments requirements
27
- req = {}
28
- if Rails.version < '3.0'
29
- route.segments.each do |segment|
30
- next unless segment.is_a?(ActionController::Routing::DynamicSegment) && segment.regexp
31
- req[segment.key.to_sym] = segment.regexp
32
- end
33
- [escape(name), route.segments.join, route.requirements.reject{|key,value| key == :controller}.inspect, req.inspect]
34
- else
35
- req = route.conditions
36
- [escape(name), route.conditions.keys.join, route.requirements.reject{|key,value| key == :controller}.inspect, req.inspect]
37
- end
27
+ req = route.conditions
28
+ [escape(name), route.conditions.keys.join, route.requirements.reject{|key,value| key == :controller}.inspect, req.inspect]
38
29
  end
39
30
  end
40
31
  end
@@ -42,13 +33,21 @@ module Footnotes
42
33
 
43
34
  module Extensions
44
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
+
45
43
  # Filter routes according to the filter sent
46
44
  #
47
45
  def filtered_routes(filter = {})
48
46
  return [] unless filter.is_a?(Hash)
49
47
  return routes.reject do |r|
50
- filter_diff = filter.diff(r.requirements)
51
- route_diff = r.requirements.diff(filter)
48
+ filter_diff = __hash_diff(filter, r.requirements)
49
+ route_diff = __hash_diff(r.requirements, filter)
50
+
52
51
  (filter_diff == filter) || (filter_diff != route_diff)
53
52
  end
54
53
  end
@@ -57,5 +56,5 @@ module Footnotes
57
56
  end
58
57
 
59
58
  if Footnotes::Notes::RoutesNote.included?
60
- ActionController::Routing::RouteSet.send :include, Footnotes::Extensions::Routes
59
+ ActionDispatch::Routing::RouteSet.send :include, Footnotes::Extensions::Routes
61
60
  end
@@ -1,3 +1,3 @@
1
1
  module Footnotes
2
- VERSION = "3.7.9"
2
+ VERSION = "4.0.0"
3
3
  end
@@ -6,7 +6,7 @@ Gem::Specification.new do |s|
6
6
  s.name = "rails-footnotes"
7
7
  s.version = Footnotes::VERSION
8
8
  s.platform = Gem::Platform::RUBY
9
- s.authors = ["Roman V. Babenko", "José Valim", "Keenan Brock", "Duane Johnson"]
9
+ s.authors = ["Roman V. Babenko", "José Valim", "Keenan Brock", "Duane Johnson", "Adrien Siami"]
10
10
  s.email = ["romanvbabenko@gmail.com"]
11
11
  s.homepage = "http://github.com/josevalim/rails-footnotes"
12
12
  s.summary = %q{Every Rails page has footnotes that gives information about your application and links back to your editor.}
@@ -14,14 +14,13 @@ Gem::Specification.new do |s|
14
14
 
15
15
  s.rubyforge_project = "rails-footnotes"
16
16
 
17
- s.add_dependency "rails", ">= 3.0.0"
17
+ s.add_dependency "rails", ">= 3.2"
18
18
 
19
- s.add_development_dependency "rails", ">= 3.0.0"
20
- s.add_development_dependency "rspec", "~> 2.9.0"
19
+ s.add_development_dependency "rspec-rails"
20
+ s.add_development_dependency "capybara"
21
21
 
22
22
  s.files = `git ls-files`.split("\n")
23
23
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
24
- s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
24
+ s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
25
25
  s.require_paths = ["lib"]
26
26
  end
27
-
@@ -3,9 +3,14 @@ require "spec_helper"
3
3
  describe Footnotes::Notes::AbstractNote do
4
4
  before do
5
5
  @note = Footnotes::Notes::AbstractNote.new
6
+ @notes = Footnotes::Filter.notes
6
7
  Footnotes::Filter.notes = [:abstract]
7
8
  end
8
9
 
10
+ after do
11
+ Footnotes::Filter.notes = @notes
12
+ end
13
+
9
14
  it {described_class.should respond_to :start!}
10
15
  it {described_class.should respond_to :close!}
11
16
  it {described_class.should respond_to :title}
@@ -0,0 +1,113 @@
1
+ require 'spec_helper'
2
+
3
+ class FootnotesController < ActionController::Base
4
+
5
+ def foo
6
+ render :text => HTML_DOCUMENT, :content_type => 'text/html'
7
+ end
8
+
9
+ def foo_holder
10
+ render :text => '<html><body><div id="footnotes_holder"></div></body></html>'
11
+ end
12
+
13
+ def foo_js
14
+ render :text => '<script></script>', :content_type => 'text/javascript'
15
+ end
16
+
17
+ def foo_download
18
+ send_file Rails.root.join('spec', 'fixtures', 'html_download.html'), :disposition => 'attachment'
19
+ end
20
+
21
+ end
22
+
23
+ describe FootnotesController do
24
+
25
+ def page
26
+ Capybara::Node::Simple.new(response.body)
27
+ end
28
+
29
+ shared_examples 'has_footnotes' do
30
+ it 'includes footnotes' do
31
+ get :foo
32
+ response.body.should have_selector('#footnotes_debug')
33
+ end
34
+ end
35
+
36
+ shared_examples 'has_no_footnotes' do
37
+ it 'does not include footnotes' do
38
+ response.body.should_not have_selector('#footnotes_debug')
39
+ end
40
+ end
41
+
42
+ it 'does not alter the page by default' do
43
+ get :foo
44
+ response.body.should == HTML_DOCUMENT
45
+ end
46
+
47
+ context 'with footnotes' do
48
+
49
+ before :all do
50
+ Footnotes.enabled = true
51
+ end
52
+
53
+ after :all do
54
+ Footnotes.enabled = false
55
+ end
56
+
57
+ describe 'by default' do
58
+ include_context 'has_footnotes'
59
+
60
+ before do
61
+ get :foo
62
+ end
63
+
64
+ it 'includes footnotes in the last div in body' do
65
+ all('body > :last-child')[0][:id].should == 'footnotes_debug'
66
+ end
67
+
68
+ it 'includes footnotes in the footnoted_holder div if present' do
69
+ get :foo_holder
70
+ response.body.should have_selector('#footnotes_holder > #footnotes_debug')
71
+ end
72
+
73
+ it 'does not alter a html file download' do
74
+ get :foo_download
75
+ response.body.should == File.open(Rails.root.join('spec', 'fixtures', 'html_download.html')).read
76
+ end
77
+ end
78
+
79
+ describe 'when request is xhr' do
80
+ include_context 'has_no_footnotes'
81
+ before do
82
+ xhr :get, :foo
83
+ end
84
+ end
85
+
86
+ describe 'when content type is javascript' do
87
+ include_context 'has_no_footnotes'
88
+ before do
89
+ get :foo_js
90
+ end
91
+ end
92
+
93
+ end
94
+
95
+ end
96
+
97
+
98
+ HTML_DOCUMENT = <<EOF
99
+ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
100
+ <html>
101
+ <head>
102
+ <title>HTML to XHTML Example: HTML page</title>
103
+ <link rel="Stylesheet" href="htmltohxhtml.css" type="text/css" media="screen">
104
+ <meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
105
+ </head>
106
+ <body>
107
+ <p>This is the HTML page. It works and is encoded just like any HTML page you
108
+ have previously done. View <a href="htmltoxhtml2.htm">the XHTML version</a> of
109
+ this page to view the difference between HTML and XHTML.</p>
110
+ <p>You will be glad to know that no changes need to be made to any of your CSS files.</p>
111
+ </body>
112
+ </html>
113
+ EOF
@@ -0,0 +1,45 @@
1
+ require 'spec_helper'
2
+ require 'stringio'
3
+
4
+ describe 'log note' do
5
+
6
+ class ApplicationController < ActionController::Base
7
+ end
8
+
9
+ controller do
10
+ def index
11
+ Rails.logger.error 'foo'
12
+ Rails.logger.warn 'bar'
13
+ render :text => '<html><head></head><body></body></html>', :content_type => 'text/html'
14
+ end
15
+ end
16
+
17
+ def page
18
+ Capybara::Node::Simple.new(response.body)
19
+ end
20
+
21
+ before :all do
22
+ Footnotes.enabled = true
23
+ end
24
+
25
+ after :all do
26
+ Footnotes.enabled = false
27
+ end
28
+
29
+ before do
30
+ @original_logger = Rails.logger
31
+ Rails.logger = Logger.new(StringIO.new)
32
+ end
33
+
34
+ after do
35
+ Rails.logger = @original_logger
36
+ end
37
+
38
+ it 'Includes the log in the response' do
39
+ get :index
40
+ log_debug = first('fieldset#log_debug_info div', :visible => false)
41
+ log_debug.should have_content('foo')
42
+ log_debug.should have_content('bar')
43
+ end
44
+
45
+ end