ginatra 3.0.1 → 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (91) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +18 -15
  3. data/.travis.yml +7 -6
  4. data/CONTRIBUTING.md +30 -0
  5. data/Gemfile +1 -9
  6. data/LICENSE.txt +30 -0
  7. data/README.md +63 -114
  8. data/Rakefile +10 -12
  9. data/bin/ginatra +79 -63
  10. data/config.ru +35 -3
  11. data/ginatra.gemspec +29 -18
  12. data/lib/ginatra.rb +161 -148
  13. data/lib/ginatra/config.rb +18 -121
  14. data/lib/ginatra/errors.rb +10 -0
  15. data/lib/ginatra/helpers.rb +154 -139
  16. data/lib/ginatra/repo.rb +67 -82
  17. data/lib/ginatra/repo_list.rb +25 -18
  18. data/lib/ginatra/repo_stats.rb +93 -0
  19. data/lib/ginatra/version.rb +4 -0
  20. data/lib/git/webby.rb +292 -0
  21. data/lib/git/webby/extensions.rb +10 -0
  22. data/lib/git/webby/http_backend.rb +177 -0
  23. data/lib/sinatra/partials.rb +1 -1
  24. data/public/css/application.css +6 -0
  25. data/public/css/custom.css +57 -0
  26. data/public/css/lib/bootstrap-responsive.min.css +9 -0
  27. data/public/css/lib/bootstrap.min.css +9 -0
  28. data/public/css/lib/highlight.css +209 -0
  29. data/public/img/glyphicons-halflings-white.png +0 -0
  30. data/public/img/glyphicons-halflings.png +0 -0
  31. data/public/img/spin.gif +0 -0
  32. data/public/js/application.js +5 -0
  33. data/public/js/custom.js +51 -0
  34. data/public/js/lib/bootstrap.min.js +6 -0
  35. data/public/js/lib/jquery.lazyload.min.js +2 -0
  36. data/public/js/lib/jquery.min.js +2 -0
  37. data/public/js/lib/jquery.pjax.js +739 -0
  38. data/repos/README.md +21 -8
  39. data/spec/ginatra/helpers_spec.rb +95 -0
  40. data/spec/ginatra/repo_list_spec.rb +66 -0
  41. data/spec/ginatra/repo_spec.rb +78 -0
  42. data/spec/ginatra/repo_stats_spec.rb +27 -0
  43. data/spec/ginatra_spec.rb +121 -0
  44. data/spec/spec_helper.rb +8 -17
  45. data/views/404.erb +18 -0
  46. data/views/500.erb +18 -0
  47. data/views/_footer.erb +7 -0
  48. data/views/_header.erb +12 -6
  49. data/views/_tree_nav.erb +53 -0
  50. data/views/atom.erb +32 -0
  51. data/views/blob.erb +27 -8
  52. data/views/commit.erb +95 -17
  53. data/views/empty_repo.erb +10 -0
  54. data/views/index.erb +27 -11
  55. data/views/layout.erb +16 -20
  56. data/views/log.erb +74 -54
  57. data/views/stats.erb +89 -0
  58. data/views/tree.erb +32 -20
  59. metadata +168 -94
  60. data/bin/ginatra-daemon +0 -87
  61. data/bin/ginatra-directory +0 -55
  62. data/bin/ginatra-server +0 -27
  63. data/bin/ginatra-setup +0 -28
  64. data/lib/ginatra/graph_commit.rb +0 -77
  65. data/public/img/add.png +0 -0
  66. data/public/img/diff.png +0 -0
  67. data/public/img/doc.png +0 -0
  68. data/public/img/rm.png +0 -0
  69. data/public/img/tree.png +0 -0
  70. data/public/src/branch-graph.js +0 -170
  71. data/public/src/colour.css +0 -86
  72. data/public/src/commit.css +0 -211
  73. data/public/src/ginatra.js +0 -7
  74. data/public/src/github.css +0 -129
  75. data/public/src/graph.css +0 -9
  76. data/public/src/highlight.pack.js +0 -1
  77. data/public/src/index.css +0 -92
  78. data/public/src/lists.css +0 -25
  79. data/public/src/raphael.js +0 -7
  80. data/public/src/reset.css +0 -49
  81. data/public/src/table.css +0 -33
  82. data/public/src/type.css +0 -30
  83. data/rackup.ru +0 -5
  84. data/spec/graph_commit_spec.rb +0 -54
  85. data/spec/repo_list_spec.rb +0 -84
  86. data/spec/repo_spec.rb +0 -61
  87. data/views/_actor_box.erb +0 -13
  88. data/views/_commit_info_box.erb +0 -27
  89. data/views/_tree_part.erb +0 -11
  90. data/views/atom.builder +0 -32
  91. data/views/graph.erb +0 -15
data/config.ru CHANGED
@@ -1,5 +1,37 @@
1
- require "ginatra"
1
+ require 'ginatra'
2
+ require 'sprockets'
2
3
 
3
- map '/' do
4
- run Ginatra::App
4
+ map '/assets' do
5
+ environment = Sprockets::Environment.new
6
+ root_path = File.dirname __FILE__
7
+ environment.append_path "#{root_path}/public/js"
8
+ environment.append_path "#{root_path}/public/css"
9
+ run environment
10
+ end
11
+
12
+ if Ginatra.config.git_clone_enabled?
13
+ require 'mkmf'
14
+ require 'git/webby'
15
+
16
+ # Make the MakeMakefile logger write file output to null
17
+ module MakeMakefile::Logging; @logfile = File::NULL; end
18
+
19
+ git_executable = find_executable 'git'
20
+ raise 'Git executable not found in PATH' if git_executable.nil?
21
+ root_path = File.dirname __FILE__
22
+
23
+ Git::Webby::HttpBackend.configure do |server|
24
+ server.project_root = "#{root_path}/repos"
25
+ server.git_path = git_executable
26
+ server.get_any_file = true
27
+ server.upload_pack = false
28
+ server.receive_pack = false
29
+ server.authenticate = false
30
+ end
31
+
32
+ run Rack::Cascade.new [Git::Webby::HttpBackend, Ginatra::App]
33
+ else
34
+ map '/' do
35
+ run Ginatra::App
36
+ end
5
37
  end
data/ginatra.gemspec CHANGED
@@ -1,21 +1,32 @@
1
- Gem::Specification.new do |s|
2
- s.name = "ginatra"
3
- s.version = "3.0.1"
4
- s.summary = "A Gitweb Clone in Sinatra and Grit"
5
- s.description = "Host your own git repository browser through the power of Sinatra and Grit"
6
- s.email = "sam@lenary.co.uk"
7
- s.homepage = "http://lenary.co.uk/ginatra"
8
- s.authors = ["Sam Elliott", "Ryan Bigg"]
9
- s.add_dependency('bundler', '~> 1.0.15')
10
- s.add_dependency('sinatra', '~> 1.2.6')
11
- s.add_dependency('grit', '~> 2.4.1')
12
- s.add_dependency('vegas', '~> 0.1.8')
13
- s.add_dependency('builder', '~> 3.0.0')
14
- s.add_dependency('erubis', '~> 2.7.0')
1
+ lib = File.expand_path('../lib', __FILE__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require 'ginatra/version'
15
4
 
5
+ Gem::Specification.new do |gem|
6
+ gem.name = "ginatra"
7
+ gem.version = Ginatra::VERSION
8
+ gem.summary = "Web interface for git repositories"
9
+ gem.description = "Git repository viewer with a rocking good web interface"
10
+ gem.homepage = "https://github.com/narkoz/ginatra"
11
+ gem.email = ["mail@narkoz.me"]
12
+ gem.authors = ["Nihad Abbasov", "Sam Elliott", "Ryan Bigg"]
16
13
 
17
- s.files = `git ls-files`.split("\n")
18
- s.test_files = `git ls-files -- spec`.split("\n")
19
- s.executables = `git ls-files -- bin`.split("\n").map {|f| File.basename(f) }
20
- s.require_paths = ["lib"]
14
+ gem.files = `git ls-files`.split($/) - ['Gemfile.lock']
15
+ gem.executables = gem.files.grep(%r{^bin/}).map { |f| File.basename(f) }
16
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
17
+ gem.require_paths = ["lib"]
18
+
19
+ gem.required_ruby_version = ">= 1.9"
20
+
21
+ gem.add_dependency 'sinatra', '~> 1.4.5'
22
+ gem.add_dependency 'rugged', '~> 0.21.3'
23
+ gem.add_dependency 'rouge', '~> 1.7.7'
24
+ gem.add_dependency 'sprockets', '~> 2.0'
25
+
26
+ gem.add_development_dependency 'rake'
27
+ gem.add_development_dependency 'rspec'
28
+ gem.add_development_dependency 'rack-test'
29
+ gem.add_development_dependency 'sinatra-contrib'
30
+ gem.add_development_dependency 'better_errors', '~> 1.1.0'
31
+ gem.add_development_dependency 'binding_of_caller'
21
32
  end
data/lib/ginatra.rb CHANGED
@@ -1,74 +1,59 @@
1
-
2
1
  require 'sinatra/base'
3
- require "sinatra/partials"
4
- require 'json'
2
+ require 'sinatra/partials'
3
+ require 'rouge'
4
+ require 'ginatra/config'
5
+ require 'ginatra/errors'
6
+ require 'ginatra/helpers'
7
+ require 'ginatra/repo'
8
+ require 'ginatra/repo_list'
9
+ require 'ginatra/repo_stats'
5
10
 
6
- # Written myself. i know, what the hell?!
7
11
  module Ginatra
12
+ # The main application class.
13
+ # Contains all the core application logic and mounted in +config.ru+ file.
14
+ class App < Sinatra::Base
15
+ helpers Helpers, Sinatra::Partials
8
16
 
9
- autoload :Config, "ginatra/config"
10
- autoload :Helpers, "ginatra/helpers"
11
- autoload :Repo, "ginatra/repo"
12
- autoload :RepoList, "ginatra/repo_list"
13
- autoload :GraphCommit, "ginatra/graph_commit"
17
+ configure do
18
+ set :host, Ginatra.config.host
19
+ set :port, Ginatra.config.port
20
+ set :public_folder, "#{settings.root}/../public"
21
+ set :views, "#{settings.root}/../views"
22
+ enable :dump_errors, :logging, :static
23
+ end
14
24
 
15
- # A standard error class for inheritance.
16
- class Error < StandardError; end
25
+ configure :development do
26
+ # Use better errors in development
27
+ require 'better_errors'
28
+ use BetterErrors::Middleware
29
+ BetterErrors.application_root = settings.root
17
30
 
18
- # An error related to a commit somewhere.
19
- class CommitsError < Error
20
- def initialize(repo)
21
- super("Something went wrong looking for the commits for #{repo}")
31
+ # Reload modified files in development
32
+ require 'sinatra/reloader'
33
+ register Sinatra::Reloader
34
+ Dir["#{settings.root}/ginatra/*.rb"].each { |file| also_reload file }
22
35
  end
23
- end
24
36
 
25
- # Error raised when commit ref passed in parameters
26
- # does not exist in repository
27
- class InvalidCommit < Error
28
- def initialize(id)
29
- super("Could not find a commit with the id of #{id}")
37
+ def cache(obj)
38
+ etag obj if settings.production?
30
39
  end
31
- end
32
-
33
- VERSION = "3.0.1"
34
-
35
- # The main application class.
36
- #
37
- # This class contains all the core application logic
38
- # and is what is mounted by the +rackup.ru+ files.
39
- class App < Sinatra::Base
40
40
 
41
- # logger that can be used with the Sinatra code
42
- def logger
43
- Ginatra::Config.logger
41
+ not_found do
42
+ erb :'404', layout: false
44
43
  end
45
44
 
46
- configure do
47
- Config.load!
48
- set :host, Ginatra::Config[:host]
49
- set :port, Ginatra::Config[:port]
50
- set :raise_errors, Proc.new { test? }
51
- set :show_exceptions, Proc.new { development? }
52
- set :dump_errors, true
53
- set :logging, Proc.new { !test? }
54
- set :static, true
55
- current_path = File.expand_path(File.dirname(__FILE__))
56
- set :public, "#{current_path}/../public"
57
- set :views, "#{current_path}/../views"
45
+ error Ginatra::RepoNotFound, Ginatra::InvalidRef,
46
+ Rugged::OdbError, Rugged::ObjectError, Rugged::InvalidError do
47
+ halt 404, erb(:'404', layout: false)
58
48
  end
59
49
 
60
- helpers Helpers, Sinatra::Partials
61
-
62
- # Let's handle a CommitsError.
63
- #
64
- # @todo prettify
65
- error CommitsError do
66
- 'No commits were returned for ' + request.uri
50
+ error 500 do
51
+ erb :'500', layout: false
67
52
  end
68
53
 
69
54
  # The root route
70
- # This works by interacting with the Ginatra::Repolist singleton.
71
55
  get '/' do
56
+ @repositories = Ginatra::RepoList.list
72
57
  erb :index
73
58
  end
74
59
 
@@ -80,47 +65,34 @@ module Ginatra
80
65
  get '/:repo.atom' do
81
66
  @repo = RepoList.find(params[:repo])
82
67
  @commits = @repo.commits
83
- return "" if @commits.empty?
84
- etag(@commits.first.id) if Ginatra::App.production?
85
- builder :atom, :layout => nil
68
+
69
+ if @commits.empty?
70
+ return ''
71
+ else
72
+ cache "#{@commits.first.oid}/atom"
73
+ content_type 'application/xml'
74
+ erb :atom, layout: false
75
+ end
86
76
  end
87
77
 
88
78
  # The html page for a +repo+.
89
79
  #
90
- # Shows the most recent commits in a log format
80
+ # Shows the most recent commits in a log format.
91
81
  #
92
82
  # @param [String] repo the repository url-sanitised-name
93
- get '/:repo' do
83
+ get '/:repo/?' do
94
84
  @repo = RepoList.find(params[:repo])
95
- @commits = @repo.commits
96
- etag(@commits.first.id) if Ginatra::App.production?
97
- erb :log
98
- end
99
85
 
100
- get '/:repo/graph' do
101
- @repo = RepoList.find(params[:repo])
102
- max_count = 650
103
- max_count = params[:max_count].to_i unless params[:max_count].nil?
104
- commits = @repo.all_commits(max_count)
105
-
106
- days = GraphCommit.index_commits(commits)
107
- @days_json = days.compact.collect{|d|[d.day,d.strftime("%b")]}.to_json
108
- @commits_json = commits.collect do |c|
109
- h = {}
110
- h[:parents] = c.parents.collect do |p|
111
- [p.id,0,0]
112
- end
113
- h[:author] = c.author.name.force_encoding("UTF-8")
114
- h[:time] = c.time
115
- h[:space] = c.space
116
- h[:refs] = c.refs.collect{|r|r.name}.join(" ") unless c.refs.nil?
117
- h[:id] = c.sha
118
- h[:date] = c.date
119
- h[:message] = c.message.force_encoding("UTF-8")
120
- h[:login] = c.author.email
121
- h
122
- end.to_json
123
- erb :graph
86
+ if @repo.branches.none?
87
+ erb :empty_repo
88
+ else
89
+ params[:page] = 1
90
+ params[:ref] = @repo.branch_exists?('master') ? 'master' : @repo.branches.first.name
91
+ @commits = @repo.commits(params[:ref])
92
+ cache "#{@commits.first.oid}/log"
93
+ @next_commits = !@repo.commits(params[:ref], 10, 10).nil?
94
+ erb :log
95
+ end
124
96
  end
125
97
 
126
98
  # The atom feed of recent commits to a certain branch of a +repo+.
@@ -130,33 +102,54 @@ module Ginatra
130
102
  get '/:repo/:ref.atom' do
131
103
  @repo = RepoList.find(params[:repo])
132
104
  @commits = @repo.commits(params[:ref])
133
- return "" if @commits.empty?
134
- etag(@commits.first.id) if Ginatra::App.production?
135
- builder :atom, :layout => nil
105
+
106
+ if @commits.empty?
107
+ return ''
108
+ else
109
+ cache "#{@commits.first.oid}/atom/ref"
110
+ content_type 'application/xml'
111
+ erb :atom, layout: false
112
+ end
136
113
  end
137
114
 
138
115
  # The html page for a given +ref+ of a +repo+.
139
116
  #
140
- # Shows the most recent commits in a log format
117
+ # Shows the most recent commits in a log format.
141
118
  #
142
119
  # @param [String] repo the repository url-sanitised-name
143
120
  # @param [String] ref the repository ref
144
121
  get '/:repo/:ref' do
145
- params[:page] = 1
146
122
  @repo = RepoList.find(params[:repo])
147
123
  @commits = @repo.commits(params[:ref])
148
- etag(@commits.first.id) if Ginatra::App.production?
124
+ cache "#{@commits.first.oid}/ref" if @commits.any?
125
+ params[:page] = 1
126
+ @next_commits = !@repo.commits(params[:ref], 10, 10).nil?
149
127
  erb :log
150
128
  end
151
129
 
130
+ # The html page for a +repo+ stats.
131
+ #
132
+ # Shows information about repository branch.
133
+ #
134
+ # @param [String] repo the repository url-sanitised-name
135
+ # @param [String] ref the repository ref
136
+ get '/:repo/stats/:ref' do
137
+ @repo = RepoList.find(params[:repo])
138
+ @stats = RepoStats.new(@repo, params[:ref])
139
+ erb :stats
140
+ end
141
+
152
142
  # The patch file for a given commit to a +repo+.
153
143
  #
154
144
  # @param [String] repo the repository url-sanitised-name
155
145
  # @param [String] commit the repository commit
156
146
  get '/:repo/commit/:commit.patch' do
157
- response['Content-Type'] = "text/plain"
158
- @repo = RepoList.find(params[:repo])
159
- @repo.git.format_patch({}, "--stdout", "-1", params[:commit])
147
+ content_type :txt
148
+ repo = RepoList.find(params[:repo])
149
+ commit = repo.commit(params[:commit])
150
+ cache "#{commit.oid}/patch"
151
+ diff = commit.parents.first.diff(commit)
152
+ diff.patch
160
153
  end
161
154
 
162
155
  # The html representation of a commit.
@@ -165,64 +158,58 @@ module Ginatra
165
158
  # @param [String] commit the repository commit
166
159
  get '/:repo/commit/:commit' do
167
160
  @repo = RepoList.find(params[:repo])
168
- @commit = @repo.commit(params[:commit]) # can also be a ref
169
- etag(@commit.id) if Ginatra::App.production?
170
- erb(:commit)
161
+ @commit = @repo.commit(params[:commit])
162
+ cache @commit.oid
163
+ erb :commit
171
164
  end
172
165
 
173
- # Download an archive of a given tree!
166
+ # The html representation of a tag.
174
167
  #
175
168
  # @param [String] repo the repository url-sanitised-name
176
- # @param [String] tree the repository tree
177
- get '/:repo/archive/:tree.tar.gz' do
178
- response['Content-Type'] = "application/x-tar-gz"
169
+ # @param [String] tag the repository tag
170
+ get '/:repo/tag/:tag' do
179
171
  @repo = RepoList.find(params[:repo])
180
- @repo.archive_tar_gz(params[:tree])
172
+ @commit = @repo.commit_by_tag(params[:tag])
173
+ cache "#{@commit.oid}/tag"
174
+ erb :commit
181
175
  end
182
176
 
183
177
  # HTML page for a given tree in a given +repo+
184
178
  #
185
- # @todo cleanup!
186
179
  # @param [String] repo the repository url-sanitised-name
187
180
  # @param [String] tree the repository tree
188
181
  get '/:repo/tree/:tree' do
189
182
  @repo = RepoList.find(params[:repo])
183
+ @tree = @repo.find_tree(params[:tree])
184
+ cache @tree.oid
190
185
 
191
- if (tag = @repo.git.rev_parse({'--verify' => ''}, "#{params[:tree]}^{tree}")).empty?
192
- # we don't have a tree.
193
- not_found
194
- else
195
- etag(tag) if Ginatra::App.production?
196
- end
197
-
198
- @tree = @repo.tree(params[:tree]) # can also be a ref (i think)
199
- @path = {}
200
- @path[:tree] = "#{params[:repo]}/tree/#{params[:tree]}"
201
- @path[:blob] = "#{params[:repo]}/blob/#{params[:tree]}"
202
- erb(:tree)
186
+ @path = {
187
+ blob: "#{params[:repo]}/blob/#{params[:tree]}",
188
+ tree: "#{params[:repo]}/tree/#{params[:tree]}"
189
+ }
190
+ erb :tree, layout: !is_pjax?
203
191
  end
204
192
 
205
193
  # HTML page for a given tree in a given +repo+.
206
194
  #
207
- # This one supports a splat parameter so you can specify a path
195
+ # This one supports a splat parameter so you can specify a path.
208
196
  #
209
- # @todo cleanup!
210
197
  # @param [String] repo the repository url-sanitised-name
211
198
  # @param [String] tree the repository tree
212
- get '/:repo/tree/:tree/*' do # for when we specify a path
199
+ get '/:repo/tree/:tree/*' do
213
200
  @repo = RepoList.find(params[:repo])
214
- @tree = @repo.tree(params[:tree])/params[:splat].first # can also be a ref (i think)
215
- if @tree.is_a?(Grit::Blob)
216
- # we need @tree to be a tree. if it's a blob, send it to the blob page
217
- # this allows people to put in the remaining part of the path to the file, rather than endless clicks like you need in github
218
- redirect "#{params[:repo]}/blob/#{params[:tree]}/#{params[:splat].first}"
219
- else
220
- etag(@tree.id) if Ginatra::App.production?
221
- @path = {}
222
- @path[:tree] = "#{params[:repo]}/tree/#{params[:tree]}/#{params[:splat].first}"
223
- @path[:blob] = "#{params[:repo]}/blob/#{params[:tree]}/#{params[:splat].first}"
224
- erb(:tree)
201
+ @tree = @repo.find_tree(params[:tree])
202
+ cache "#{@tree.oid}/#{params[:splat].first}"
203
+
204
+ @tree.walk(:postorder) do |root, entry|
205
+ @tree = @repo.lookup entry[:oid] if "#{root}#{entry[:name]}" == params[:splat].first
225
206
  end
207
+
208
+ @path = {
209
+ blob: "#{params[:repo]}/blob/#{params[:tree]}/#{params[:splat].first}",
210
+ tree: "#{params[:repo]}/tree/#{params[:tree]}/#{params[:splat].first}"
211
+ }
212
+ erb :tree, layout: !is_pjax?
226
213
  end
227
214
 
228
215
  # HTML page for a given blob in a given +repo+
@@ -231,49 +218,75 @@ module Ginatra
231
218
  # @param [String] tree the repository tree
232
219
  get '/:repo/blob/:blob' do
233
220
  @repo = RepoList.find(params[:repo])
234
- @blob = @repo.blob(params[:blob])
235
- etag(@blob.id) if Ginatra::App.production?
236
- erb(:blob)
221
+ @tree = @repo.lookup(params[:tree])
222
+
223
+ @tree.walk(:postorder) do |root, entry|
224
+ @blob = entry if "#{root}#{entry[:name]}" == params[:splat].first
225
+ end
226
+
227
+ cache @blob[:oid]
228
+ erb :blob, layout: !is_pjax?
237
229
  end
238
230
 
239
231
  # HTML page for a given blob in a given repo.
240
232
  #
241
233
  # Uses a splat param to specify a blob path.
242
234
  #
243
- # @todo cleanup!
244
235
  # @param [String] repo the repository url-sanitised-name
245
236
  # @param [String] tree the repository tree
246
237
  get '/:repo/blob/:tree/*' do
247
238
  @repo = RepoList.find(params[:repo])
248
- @blob = @repo.tree(params[:tree])/params[:splat].first
249
- if @blob.is_a?(Grit::Tree)
250
- # as above, we need @blob to be a blob. if it's a tree, send it to the tree page
251
- # this allows people to put in the remaining part of the path to the folder, rather than endless clicks like you need in github
252
- redirect "/#{params[:repo]}/tree/#{params[:tree]}/#{params[:splat].first}"
239
+ @tree = @repo.find_tree(params[:tree])
240
+
241
+ @tree.walk(:postorder) do |root, entry|
242
+ @blob = entry if "#{root}#{entry[:name]}" == params[:splat].first
243
+ end
244
+
245
+ cache "#{@blob[:oid]}/#{@tree.oid}"
246
+ erb :blob, layout: !is_pjax?
247
+ end
248
+
249
+ # HTML page for a raw blob contents in a given repo.
250
+ #
251
+ # Uses a splat param to specify a blob path.
252
+ #
253
+ # @param [String] repo the repository url-sanitised-name
254
+ # @param [String] tree the repository tree
255
+ get '/:repo/raw/:tree/*' do
256
+ @repo = RepoList.find(params[:repo])
257
+ @tree = @repo.find_tree(params[:tree])
258
+
259
+ @tree.walk(:postorder) do |root, entry|
260
+ @blob = entry if "#{root}#{entry[:name]}" == params[:splat].first
261
+ end
262
+
263
+ cache "#{@blob[:oid]}/#{@tree.oid}/raw"
264
+ blob = @repo.find_blob @blob[:oid]
265
+ if blob.binary?
266
+ content_type 'application/octet-stream'
267
+ blob.text
253
268
  else
254
- etag(@blob.id) if Ginatra::App.production?
255
- erb(:blob)
269
+ content_type :txt
270
+ blob.text
256
271
  end
257
272
  end
258
273
 
259
- # pagination route for the commits to a given ref in a +repo+.
274
+ # Pagination route for the commits to a given ref in a +repo+.
260
275
  #
261
- # @todo cleanup!
262
276
  # @param [String] repo the repository url-sanitised-name
263
277
  # @param [String] ref the repository ref
264
- get '/:repo/:ref/:page' do
265
- pass unless params[:page] =~ /^(\d)+$/
278
+ get '/:repo/:ref/page/:page' do
279
+ pass unless params[:page] =~ /\A\d+\z/
266
280
  params[:page] = params[:page].to_i
267
281
  @repo = RepoList.find(params[:repo])
268
282
  @commits = @repo.commits(params[:ref], 10, (params[:page] - 1) * 10)
269
- @next_commits = !@repo.commits(params[:ref], 10, params[:page] * 10).empty?
283
+ cache "#{@commits.first.oid}/page/#{params[:page]}/ref/#{params[:ref]}" if @commits.any?
284
+ @next_commits = !@repo.commits(params[:ref], 10, params[:page] * 10).nil?
270
285
  if params[:page] - 1 > 0
271
286
  @previous_commits = !@repo.commits(params[:ref], 10, (params[:page] - 1) * 10).empty?
272
287
  end
273
- @separator = @next_commits && @previous_commits
274
288
  erb :log
275
289
  end
276
290
 
277
291
  end # App
278
-
279
292
  end # Ginatra