rack_revision_info 0.3.5
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.
- data/lib/rack_revision_info.rb +77 -0
- data/lib/rack_revision_info/nokogiri_backend.rb +14 -0
- data/spec/spec_rack_revision_info.rb +111 -0
- metadata +57 -0
@@ -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, @revision_info) }
|
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,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,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("Revision #{REV} (#{DATE.strftime(Rack::RevisionInfo::DATETIME_FORMAT)})")}.*#{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.5
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Marcin Kulik
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-10-18 00:00:00 +02: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.3
|
53
|
+
signing_key:
|
54
|
+
specification_version: 3
|
55
|
+
summary: Rack middleware showing current git (or svn) revision number of application
|
56
|
+
test_files: []
|
57
|
+
|