rack-git 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ vendor
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in rack-svn.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Takashi Kato
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,31 @@
1
+ # Rack::Git
2
+
3
+ rack application (not a middleware) serves contents in a git repository directly ( like Rack::File)
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'rack-git'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install rack-git
18
+
19
+ ## Usage
20
+
21
+ config.ru
22
+
23
+ run Rack::Git::Directory
24
+
25
+ ## Contributing
26
+
27
+ 1. Fork it
28
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
29
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
30
+ 4. Push to the branch (`git push origin my-new-feature`)
31
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
@@ -0,0 +1,160 @@
1
+ require 'time'
2
+ require 'rack/utils'
3
+ require 'rack/mime'
4
+ require 'rack/git/file'
5
+
6
+ module Rack
7
+ # Rack::Directory serves entries below the +root+ given, according to the
8
+ # path info of the Rack request. If a directory is found, the file's contents
9
+ # will be presented in an html based index. If a file is found, the env will
10
+ # be passed to the specified +app+.
11
+ #
12
+ # If +app+ is not specified, a Rack::File of the same +root+ will be used.
13
+
14
+ module Git
15
+ class Directory
16
+ DIR_FILE = "<tr><td class='name'><a href='%s'>%s</a></td><td class='size'>%s</td><td class='type'>%s</td><td class='mtime'>%s</td></tr>"
17
+ DIR_PAGE = <<-PAGE
18
+ <html><head>
19
+ <title>%s</title>
20
+ <meta http-equiv="content-type" content="text/html; charset=utf-8" />
21
+ <style type='text/css'>
22
+ table { width:100%%; }
23
+ .name { text-align:left; }
24
+ .size, .mtime { text-align:right; }
25
+ .type { width:11em; }
26
+ .mtime { width:15em; }
27
+ </style>
28
+ </head><body>
29
+ <h1>%s</h1>
30
+ <hr />
31
+ <table>
32
+ <tr>
33
+ <th class='name'>Name</th>
34
+ <th class='size'>Size</th>
35
+ <th class='type'>Type</th>
36
+ <th class='mtime'>Last Modified</th>
37
+ </tr>
38
+ %s
39
+ </table>
40
+ <hr />
41
+ </body></html>
42
+ PAGE
43
+
44
+ attr_reader :files
45
+ attr_accessor :root, :path
46
+
47
+ def initialize(root, app=nil)
48
+ @root = root
49
+ @app = app || Rack::Git::File.new(@root)
50
+ @repo = Grit::Repo.new(@root)
51
+ end
52
+
53
+ def call(env)
54
+ dup._call(env)
55
+ end
56
+
57
+ F = ::File
58
+
59
+ def _call(env)
60
+ @env = env
61
+ @branch_name = env['PATH_PREFIX'] || "master"
62
+ @script_name = env['SCRIPT_NAME']
63
+ @path_info = Utils.unescape(env['PATH_INFO']).sub(/\/$/,"").sub(/^\//,"")
64
+
65
+ if forbidden = check_forbidden
66
+ forbidden
67
+ else
68
+ list_path
69
+ end
70
+ end
71
+
72
+ def check_forbidden
73
+ return unless @path_info.include? ".."
74
+
75
+ body = "Forbidden\n"
76
+ size = Rack::Utils.bytesize(body)
77
+ return [403, {"Content-Type" => "text/plain",
78
+ "Content-Length" => size.to_s,
79
+ "X-Cascade" => "pass"}, [body]]
80
+ end
81
+
82
+ def list_directory
83
+ @files = [['../','Parent Directory','','','']]
84
+
85
+ #context.list(@path ,"HEAD") do |name,dirent,lock,abs_path|
86
+ # basename = F.basename(name)
87
+ # ext = F.extname(name)
88
+ #
89
+ # url = F.join(@script_name, @path_info, basename)
90
+ # size = dirent.size
91
+ # type = dirent.directory? ? 'directory' : Mime.mime_type(ext)
92
+ # size = dirent.directory? ? '-' : filesize_format(size)
93
+ # mtime = dirent.time2.httpdate
94
+ # url << '/' if dirent.directory?
95
+ # basename << '/' if dirent.directory?
96
+ #
97
+ # @files << [ url, basename, size, type, mtime ]
98
+ #end
99
+ return [ 200, {'Content-Type'=>'text/html; charset=utf-8'}, self ]
100
+ end
101
+
102
+ #def stat(node, max = 10)
103
+ # F.stat(node)
104
+ #rescue Errno::ENOENT, Errno::ELOOP
105
+ # return nil
106
+ #end
107
+
108
+ # TODO: add correct response if not readable, not sure if 404 is the best
109
+ # option
110
+ def list_path
111
+
112
+ history = @repo.log(@branch_name, @path_info, :max_count => 1, :skip => 0)
113
+ if history[0] == nil
114
+ return entity_not_found
115
+ end
116
+ entry = history[0].tree / @path_info
117
+
118
+ if entry.is_a?(Grit::Blob)
119
+ @app.call(@env)
120
+ elsif entry.is_a?(Grit::Tree)
121
+ list_directory
122
+ else
123
+ entity_not_found
124
+ end
125
+ end
126
+
127
+ def entity_not_found
128
+ body = "Entity not found: #{@path_info}\n"
129
+ size = Rack::Utils.bytesize(body)
130
+ return [404, {"Content-Type" => "text/plain",
131
+ "Content-Length" => size.to_s,
132
+ "X-Cascade" => "pass"}, [body]]
133
+ end
134
+
135
+ def each
136
+ show_path = @path_info.sub(/^#{@root}/,'')
137
+ files = @files.map{|f| DIR_FILE % f }*"\n"
138
+ page = DIR_PAGE % [ show_path, show_path , files ]
139
+ page.each_line{|l| yield l }
140
+ end
141
+
142
+ # Stolen from Ramaze
143
+
144
+ FILESIZE_FORMAT = [
145
+ ['%.1fT', 1 << 40],
146
+ ['%.1fG', 1 << 30],
147
+ ['%.1fM', 1 << 20],
148
+ ['%.1fK', 1 << 10],
149
+ ]
150
+
151
+ def filesize_format(int)
152
+ FILESIZE_FORMAT.each do |format, size|
153
+ return format % (int.to_f / size) if int >= size
154
+ end
155
+
156
+ int.to_s + 'B'
157
+ end
158
+ end
159
+ end
160
+ end
@@ -0,0 +1,84 @@
1
+ require 'time'
2
+ require 'rack/utils'
3
+ require 'rack/mime'
4
+ require 'grit'
5
+
6
+ module Rack
7
+ module Git
8
+ class File
9
+ attr_accessor :root
10
+ attr_accessor :path
11
+
12
+ alias :to_path :path
13
+
14
+ def initialize(root)
15
+ @root = root
16
+ @repo = Grit::Repo.new(@root)
17
+ end
18
+
19
+ def call(env)
20
+ dup._call(env)
21
+ end
22
+
23
+ F = ::File
24
+
25
+ def _call(env)
26
+ @branch_name = env['PATH_PREFIX'].sub(/^\//,"") || "master"
27
+ @path_info = Rack::Utils.unescape(env["PATH_INFO"]).sub(/^\//,"")
28
+ return forbidden if @path_info.include? ".."
29
+
30
+ begin
31
+ serving
32
+ rescue SystemCallError
33
+ not_found
34
+ end
35
+ end
36
+
37
+ def forbidden
38
+ body = "Forbidden\n"
39
+ [403, {"Content-Type" => "text/plain",
40
+ "Content-Length" => body.size.to_s,
41
+ "X-Cascade" => "pass"},
42
+ [body]]
43
+ end
44
+
45
+ # NOTE:
46
+ # We check via File::size? whether this file provides size info
47
+ # via stat (e.g. /proc files often don't), otherwise we have to
48
+ # figure it out by reading the whole file into memory. And while
49
+ # we're at it we also use this as body then.
50
+
51
+ def serving
52
+ history = @repo.log(@branch_name, @path_info, :max_count => 1, :skip => 0)
53
+ blob = history[0].tree / @path_info
54
+
55
+ body = blob.data
56
+ last_modified = history[0].authored_date
57
+ size = blob.size
58
+
59
+ [200, {
60
+ "Last-Modified" => last_modified.httpdate,
61
+ "Content-Type" => Rack::Mime.mime_type(F.extname(@path_info), 'text/plain'),
62
+ "Content-Length" => size.to_s
63
+ }, body]
64
+ end
65
+
66
+ def not_found
67
+ body = "File not found: #{@path_info}\n"
68
+ [404, {"Content-Type" => "text/plain",
69
+ "Content-Length" => body.size.to_s,
70
+ "X-Cascade" => "pass"},
71
+ [body]]
72
+ end
73
+
74
+ def each
75
+ body = @repo.tree(@branch_name).contents.select{|c| c.name == @path_info}[0].data
76
+ StringIO.new(body, "rb") do |file|
77
+ while part = file.read(8192)
78
+ yield part
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,5 @@
1
+ module Rack
2
+ module Git
3
+ VERSION = "0.0.1"
4
+ end
5
+ end
data/rack-git.gemspec ADDED
@@ -0,0 +1,18 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/rack/git/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["tohosaku"]
6
+ gem.email = ["ny@cosmichorror.org"]
7
+ gem.description = %q{rack application (not a middleware) serves contents in a git repository directly }
8
+ gem.summary = %q{rack application (not a middleware) serves contents in a git repository directly }
9
+ gem.homepage = ""
10
+
11
+ gem.files = `git ls-files`.split($\)
12
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
13
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
14
+ gem.name = "rack-git"
15
+ gem.require_paths = ["lib"]
16
+ gem.version = Rack::Git::VERSION
17
+ gem.add_dependency('grit')
18
+ end
metadata ADDED
@@ -0,0 +1,87 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rack-git
3
+ version: !ruby/object:Gem::Version
4
+ hash: 29
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ version: 0.0.1
11
+ platform: ruby
12
+ authors:
13
+ - tohosaku
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2012-08-16 00:00:00 Z
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: grit
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ hash: 3
29
+ segments:
30
+ - 0
31
+ version: "0"
32
+ type: :runtime
33
+ version_requirements: *id001
34
+ description: "rack application (not a middleware) serves contents in a git repository directly "
35
+ email:
36
+ - ny@cosmichorror.org
37
+ executables: []
38
+
39
+ extensions: []
40
+
41
+ extra_rdoc_files: []
42
+
43
+ files:
44
+ - .gitignore
45
+ - Gemfile
46
+ - LICENSE
47
+ - README.md
48
+ - Rakefile
49
+ - lib/rack/git/directory.rb
50
+ - lib/rack/git/file.rb
51
+ - lib/rack/git/version.rb
52
+ - rack-git.gemspec
53
+ homepage: ""
54
+ licenses: []
55
+
56
+ post_install_message:
57
+ rdoc_options: []
58
+
59
+ require_paths:
60
+ - lib
61
+ required_ruby_version: !ruby/object:Gem::Requirement
62
+ none: false
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ hash: 3
67
+ segments:
68
+ - 0
69
+ version: "0"
70
+ required_rubygems_version: !ruby/object:Gem::Requirement
71
+ none: false
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ hash: 3
76
+ segments:
77
+ - 0
78
+ version: "0"
79
+ requirements: []
80
+
81
+ rubyforge_project:
82
+ rubygems_version: 1.8.17
83
+ signing_key:
84
+ specification_version: 3
85
+ summary: rack application (not a middleware) serves contents in a git repository directly
86
+ test_files: []
87
+