rack-revision-info 0.3.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,14 @@
1
+ require 'nokogiri'
2
+
3
+ # extending Nokogiri with 'append' and 'prepend' methods
4
+
5
+ class Nokogiri::XML::Element
6
+ def append(data)
7
+ self.inner_html = inner_html + data
8
+ end
9
+
10
+ def prepend(data)
11
+ self.inner_html = data + inner_html
12
+ end
13
+ end
14
+
@@ -0,0 +1,77 @@
1
+ module Rack
2
+ class RevisionInfo
3
+ INJECT_ACTIONS = [:after, :before, :append, :prepend, :swap, :inner_html]
4
+
5
+ def initialize(app, opts={})
6
+ @app = app
7
+ path = opts[:path] or raise ArgumentError, "You must specify directory of your local repository!"
8
+ revision, date = get_revision_info(path, opts)
9
+ @revision_info = "#{get_revision_label(opts)} #{revision || 'unknown'}"
10
+ @revision_info << " (#{date.strftime(get_date_format(opts))})" if date
11
+ @action = (opts.keys & INJECT_ACTIONS).first
12
+ if @action
13
+ require ::File.join(::File.dirname(__FILE__), 'rack-revision-info', 'nokogiri_backend')
14
+ @selector = opts[@action]
15
+ @action = :inner_html= if @action == :inner_html
16
+ end
17
+ end
18
+
19
+ def call(env)
20
+ status, headers, body = @app.call(env)
21
+ if headers['Content-Type'].to_s.include?('text/html') && !Rack::Request.new(env).xhr?
22
+ body = body.inject("") { |acc, line| acc + line }
23
+ begin
24
+ if @action
25
+ doc = Nokogiri.parse(body)
26
+ elements = doc.css(@selector)
27
+ if elements.size > 0
28
+ elements.each { |e| e.send(@action, "<span class=\"rack-revision-info\">#{@revision_info}</span>") }
29
+ body = doc.to_s
30
+ end
31
+ end
32
+ rescue => e
33
+ puts e
34
+ puts e.backtrace
35
+ end
36
+ body << %(\n<!-- #{@revision_info} -->\n)
37
+ headers["Content-Length"] = body.size.to_s
38
+ body = [body]
39
+ end
40
+ [status, headers, body]
41
+ end
42
+
43
+ protected
44
+
45
+ def get_revision_label(opts={})
46
+ opts[:revision_label] || 'Revision'
47
+ end
48
+
49
+ def get_date_format(opts={})
50
+ opts[:date_format] || "%Y-%m-%d %H:%M:%S %Z"
51
+ end
52
+
53
+ def get_revision_info(path, opts={})
54
+ case detect_type(path)
55
+ when :git
56
+ revision_regex_extra = opts[:short_git_revisions] ? '{8}' : '+'
57
+ info = `cd #{path}; LC_ALL=C git log -1 --pretty=medium`
58
+ revision = info[/commit\s([a-z0-9]#{revision_regex_extra})/, 1]
59
+ date = (d = info[/Date:\s+(.+)$/, 1]) && (DateTime.parse(d) rescue nil)
60
+ when :svn
61
+ info = `cd #{path}; LC_ALL=C svn info`
62
+ revision = info[/Revision:\s(\d+)$/, 1]
63
+ date = (d = info[/Last Changed Date:\s([^\(]+)/, 1]) && (DateTime.parse(d.strip) rescue nil)
64
+ else
65
+ revision, date = nil, nil
66
+ end
67
+ [revision, date]
68
+ end
69
+
70
+ def detect_type(path)
71
+ return :git if ::File.directory?(::File.join(path, ".git"))
72
+ return :svn if ::File.directory?(::File.join(path, ".svn"))
73
+ :unknown
74
+ end
75
+
76
+ end
77
+ end
@@ -0,0 +1,111 @@
1
+ require 'rubygems'
2
+ require 'spec'
3
+ require 'rack/builder'
4
+ require 'rack/mock'
5
+ require File.join(File.dirname(__FILE__), '..', 'lib', 'rack-revision-info.rb')
6
+
7
+ class Rack::RevisionInfo
8
+ DATETIME_FORMAT = "%Y-%m-%d %H:%M:%S %Z"
9
+ def get_revision_info(path, opts={})
10
+ [(opts[:short_git_revisions] ? SHORT_GIT_REV : REV), DATE]
11
+ end
12
+ def get_revision_label(opts={})
13
+ opts[:revision_label] or REV_LABEL
14
+ end
15
+ def get_date_format(opts={})
16
+ opts[:date_format] or DATETIME_FORMAT
17
+ end
18
+ end
19
+
20
+ describe "Rack::RevisionInfo" do
21
+ REV = "a4jf64jf4hff"
22
+ SHORT_GIT_REV = "a4jf64jf"
23
+ REV_LABEL = "Revision"
24
+ DATE = DateTime.now
25
+
26
+ it "should append revision info in html comment" do
27
+ app = Rack::Builder.new do
28
+ use Rack::RevisionInfo, :path => "/some/path/to/repo"
29
+ run lambda { |env| [200, { 'Content-Type' => 'text/html' }, ["<html><head></head><body>Hello, World!</body></html>"]] }
30
+ end
31
+ response = Rack::MockRequest.new(app).get('/')
32
+ response.body.should match(/#{Regexp.escape("<!-- Revision #{REV} (#{DATE.strftime(Rack::RevisionInfo::DATETIME_FORMAT)}) -->")}/m)
33
+ end
34
+
35
+ it "should append customised revision info in html comment" do
36
+ custom_date_format = "%d-%m-%Y %H:%M:%S"
37
+ app = Rack::Builder.new do
38
+ use Rack::RevisionInfo, :path => "/some/path/to/repo", :revision_label => "Rev", :date_format => custom_date_format
39
+ run lambda { |env| [200, { 'Content-Type' => 'text/html' }, ["<html><head></head><body>Hello, World!</body></html>"]] }
40
+ end
41
+ response = Rack::MockRequest.new(app).get('/')
42
+ response.body.should match(/#{Regexp.escape("<!-- Rev #{REV} (#{DATE.strftime(custom_date_format)}) -->")}/)
43
+ end
44
+
45
+ it "should append customised git specific revision info in html comment" do
46
+ custom_date_format = "%d-%m-%Y %H:%M:%S"
47
+ app = Rack::Builder.new do
48
+ use Rack::RevisionInfo, :path => "/some/path/to/repo", :revision_label => "Rev", :date_format => custom_date_format, :short_git_revisions => true
49
+ run lambda { |env| [200, { 'Content-Type' => 'text/html' }, ["<html><head></head><body>Hello, World!</body></html>"]] }
50
+ end
51
+ response = Rack::MockRequest.new(app).get('/')
52
+ response.body.should match(/#{Regexp.escape("<!-- Rev #{SHORT_GIT_REV} (#{DATE.strftime(custom_date_format)}) -->")}/)
53
+ end
54
+
55
+ it "shouldn't append revision info for non-html content-types" do
56
+ app = Rack::Builder.new do
57
+ use Rack::RevisionInfo, :path => "/some/path/to/repo"
58
+ run lambda { |env| [200, { 'Content-Type' => 'text/plain' }, ["Hello, World!"]] }
59
+ end
60
+ response = Rack::MockRequest.new(app).get('/')
61
+ response.body.should_not match(/#{Regexp.escape('<!-- Revision ')}/m)
62
+ end
63
+
64
+ it "shouldn't append revision info for xhr requests" do
65
+ app = Rack::Builder.new do
66
+ use Rack::RevisionInfo, :path => "/some/path/to/repo"
67
+ run lambda { |env| [200, { 'Content-Type' => 'text/html' }, ["<html><head></head><body>Hello, World!</body></html>"]] }
68
+ end
69
+ response = Rack::MockRequest.new(app).get('/', "HTTP_X_REQUESTED_WITH" => "XMLHttpRequest")
70
+ response.body.should_not match(/#{Regexp.escape('<!-- Revision ')}/m)
71
+ end
72
+
73
+ it "should raise exeption when no path given" do
74
+ app = Rack::Builder.new do
75
+ use Rack::RevisionInfo
76
+ run lambda { |env| [200, {'Content-Type' => 'text/plain'}, ["Hello, World!"]] }
77
+ end
78
+ lambda do
79
+ response = Rack::MockRequest.new(app).get('/')
80
+ end.should raise_error(ArgumentError)
81
+ end
82
+
83
+ it "should inject revision info into DOM" do
84
+ Rack::RevisionInfo::INJECT_ACTIONS.each do |action|
85
+ app = Rack::Builder.new do
86
+ use Rack::RevisionInfo, :path => "/some/path/to/repo", action => "#footer"
87
+ run lambda { |env| [200, { 'Content-Type' => 'text/html' }, [%q{<html><head></head><body>Hello, World!<div id="footer">Foota</div></body></html>}]] }
88
+ end
89
+ response = Rack::MockRequest.new(app).get('/')
90
+ response.body.should match(/#{Regexp.escape("<span class=\"rack-revision-info\">Revision #{REV} (#{DATE.strftime(Rack::RevisionInfo::DATETIME_FORMAT)})</span>")}.*#{Regexp.escape("</body>")}/m)
91
+ end
92
+ end
93
+
94
+ it "shouldn't inject revision info into DOM if unknown action" do
95
+ app = Rack::Builder.new do
96
+ use Rack::RevisionInfo, :path => "/some/path/to/repo", :what_what => "#footer"
97
+ run lambda { |env| [200, { 'Content-Type' => 'text/html' }, [%q{<html><head></head><body>Hello, World!<div id="footer">Foota</div></body></html>}]] }
98
+ end
99
+ response = Rack::MockRequest.new(app).get('/')
100
+ response.body.should_not match(/#{Regexp.escape("Revision #{REV} (#{DATE.strftime(Rack::RevisionInfo::DATETIME_FORMAT)})")}.*#{Regexp.escape("</body>")}/m)
101
+ end
102
+
103
+ it "shouldn't escape backslashes" do # hpricot was doing this :|
104
+ app = Rack::Builder.new do
105
+ use Rack::RevisionInfo, :path => "/some/path/to/repo", :inner_html => "#footer"
106
+ run lambda { |env| [200, { 'Content-Type' => 'text/html' }, [%q{<html><head></head><body><input type="text" name="foo" value="\" /><div id="footer">Foota</div></body></html>}]] }
107
+ end
108
+ response = Rack::MockRequest.new(app).get('/')
109
+ response.body.should_not match(/value="\\\\"/m)
110
+ end
111
+ end
metadata ADDED
@@ -0,0 +1,57 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rack-revision-info
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.3.6
5
+ platform: ruby
6
+ authors:
7
+ - Marcin Kulik
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-10-30 00:00:00 +01:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description:
17
+ email: marcin.kulik@gmail.com
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files: []
23
+
24
+ files:
25
+ - lib/rack-revision-info.rb
26
+ - lib/rack-revision-info/nokogiri_backend.rb
27
+ - spec/spec-rack-revision-info.rb
28
+ has_rdoc: true
29
+ homepage: http://sickill.net
30
+ licenses: []
31
+
32
+ post_install_message:
33
+ rdoc_options: []
34
+
35
+ require_paths:
36
+ - lib
37
+ required_ruby_version: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ version: "0"
42
+ version:
43
+ required_rubygems_version: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: "0"
48
+ version:
49
+ requirements: []
50
+
51
+ rubyforge_project:
52
+ rubygems_version: 1.3.5
53
+ signing_key:
54
+ specification_version: 3
55
+ summary: Rack middleware showing current git (or svn) revision number of application
56
+ test_files: []
57
+