libdolt 0.26.0 → 0.27.0
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/.travis.yml +2 -1
- data/Gemfile.lock +1 -1
- data/Readme.md +184 -75
- data/lib/libdolt/controller_actions.rb +191 -0
- data/lib/libdolt/version.rb +1 -1
- data/lib/libdolt.rb +1 -1
- data/test/libdolt/controller_actions_test.rb +475 -0
- data/test/test_helper.rb +127 -9
- metadata +4 -3
- data/lib/libdolt/gitorious_repo_resolver.rb +0 -29
data/.travis.yml
CHANGED
data/Gemfile.lock
CHANGED
data/Readme.md
CHANGED
|
@@ -1,102 +1,211 @@
|
|
|
1
|
-
#
|
|
1
|
+
# libdolt - Git repository browser internals
|
|
2
2
|
|
|
3
3
|
<a href="http://travis-ci.org/cjohansen/libdolt" class="travis">
|
|
4
4
|
<img src="https://secure.travis-ci.org/cjohansen/libdolt.png">
|
|
5
5
|
</a>
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
rendering, commit log and blame.
|
|
7
|
+
`libdolt` is all the reusable internal workings of the
|
|
8
|
+
[Dolt repository browser](https://gitorious.org/gitorious/dolt). It provides all
|
|
9
|
+
the mechanics for retrieving the data you need display Git trees, blobs, blame
|
|
10
|
+
and more, and also includes tools to render them in a web context.
|
|
12
11
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
templates outputting HTML.
|
|
12
|
+
`libdolt` does not depend on Sinatra, or any other web context, so it can easily
|
|
13
|
+
be embedded in other frameworks/apps. Most notably, `libdolt` is used as the
|
|
14
|
+
repository browser in [Gitorious 3](https://gitorious.org/gitorious/mainline),
|
|
15
|
+
and as a stand-alone repository browser in [Dolt](https://gitorious.org/gitorious/dolt).
|
|
18
16
|
|
|
19
|
-
|
|
20
|
-
[Gitorious](http://gitorious.org) software.
|
|
17
|
+
## Installing libdolt
|
|
21
18
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
To install `dolt` you need Ruby, RubyGems and Python development files. The
|
|
25
|
-
Python development files are required to support Pygments syntax highlighting.
|
|
26
|
-
|
|
27
|
-
Note: Dolt uses [libgit2](http://libgit2.github.com) and its Ruby bindings,
|
|
28
|
-
[Rugged](http://github.com/libgit2/rugged) for Git access where
|
|
29
|
-
feasible. Currently, ``Dolt`` relies on a version of `Rugged` that is not
|
|
30
|
-
yet released, so you have to build it yourself.
|
|
31
|
-
[See em-rugged instructions](http://github.com/cjohansen/em-rugged).
|
|
19
|
+
libdolt depends on two system packages to do its job.
|
|
32
20
|
|
|
33
21
|
### Systems using apt (Debian/Ubuntu, others)
|
|
34
22
|
|
|
35
|
-
# 1) Install
|
|
36
|
-
sudo apt-get install
|
|
23
|
+
# 1) Install Python development files
|
|
24
|
+
sudo apt-get install -y python-dev libicu-dev
|
|
37
25
|
|
|
38
|
-
# 2) Install
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
# 3) Install dolt. This may or may not require the use of sudo, depending on
|
|
42
|
-
# how you installed Ruby. This step assumes that you already built and
|
|
43
|
-
# installed em-rugged as explained above.
|
|
44
|
-
sudo gem install dolt
|
|
26
|
+
# 2) Install dolt. This may or may not require the use of sudo, depending on
|
|
27
|
+
# how you installed Ruby.
|
|
28
|
+
gem install libdolt
|
|
45
29
|
|
|
46
30
|
### Systems using yum (Fedora/CentOS/RedHat, others)
|
|
47
31
|
|
|
48
|
-
# 1) Install
|
|
49
|
-
sudo yum install
|
|
50
|
-
|
|
51
|
-
# 2) Install Python development files
|
|
52
|
-
sudo yum install python-devel
|
|
32
|
+
# 1) Install Python development files
|
|
33
|
+
sudo yum install -y python-devel libicu-devel
|
|
53
34
|
|
|
54
35
|
# 3) Install dolt. This may or may not require the use of sudo, depending on
|
|
55
|
-
# how you installed Ruby.
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
36
|
+
# how you installed Ruby.
|
|
37
|
+
gem install dolt
|
|
38
|
+
|
|
39
|
+
## API
|
|
40
|
+
|
|
41
|
+
`libdolt` provides two main abstractions you may be interested in:
|
|
42
|
+
|
|
43
|
+
* `Dolt::RepositoryLookup` provides an API that will fetch various bits of
|
|
44
|
+
information from your git repository, and returns a hash of data. This hash
|
|
45
|
+
can typically be used for rendering in a template of some sort.
|
|
46
|
+
* `Dolt::ControllerActions` provides a web front-end to the repository lookup.
|
|
47
|
+
It will use the lookup class to fetch the information it needs, and then it
|
|
48
|
+
will render them using [Tiltout](https://gitorious.org/gitorious/tiltout). If
|
|
49
|
+
you're looking to make a web-based repository browser, you can use this class
|
|
50
|
+
in a Sinatra, Rack or Rails application, provide you own templates etc.
|
|
51
|
+
|
|
52
|
+
## Repository lookups
|
|
53
|
+
|
|
54
|
+
The `Dolt::RepositoryLookup` class provides many methods that use
|
|
55
|
+
[libgit2/Rugged](https://github.com/libgit2/rugged) in conjunction with the
|
|
56
|
+
classes found in `lib/libdolt/git` to fetch, consolidate and prepare Git
|
|
57
|
+
repository data in a display-friendly way. All methods return a hash.
|
|
58
|
+
|
|
59
|
+
The repository lookup class depends on a "repository resolver". This is an
|
|
60
|
+
object that can take a string from the URL, such as `"gitorious/mainline"` and
|
|
61
|
+
return a usable repository object. The repository object is expected to conform
|
|
62
|
+
to the `Dolt::Git::Repository` interface. Typically you will want to instantiate
|
|
63
|
+
this object, but you can in theory provide your own implementation, so long as
|
|
64
|
+
you maintain the interface.
|
|
65
|
+
|
|
66
|
+
Repository resolvers are quite simple animals. Here's an example of how to make
|
|
67
|
+
Dolt work with Gitorious' Repository model objects:
|
|
68
|
+
|
|
69
|
+
```rb
|
|
70
|
+
module Gitorious
|
|
71
|
+
module Dolt
|
|
72
|
+
class RepositoryResolver
|
|
73
|
+
# How you initialize your objects is up to you - Dolt doesn't care, it
|
|
74
|
+
# only ever sees the instance, not the class itself.
|
|
75
|
+
def initialize(scope = ::Repository)
|
|
76
|
+
@scope = scope
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def resolve(repo)
|
|
80
|
+
repository = @scope.find_by_path(repo)
|
|
81
|
+
raise ActiveRecord::RecordNotFound.new if repository.nil?
|
|
82
|
+
Gitorious::Dolt::Repository.new(repository)
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Common data
|
|
90
|
+
|
|
91
|
+
All actions return a hash that include these keys:
|
|
92
|
+
|
|
93
|
+
* `path` - The repository-relative path
|
|
94
|
+
* `ref` - The ref or object id
|
|
95
|
+
|
|
96
|
+
### Example: Looking up a tree
|
|
97
|
+
|
|
98
|
+
```rb
|
|
99
|
+
resolver = Gitorious::Dolt::RepositoryResolver.new
|
|
100
|
+
lookup = Dolt::RepositoryLookup.new(resolver)
|
|
101
|
+
|
|
102
|
+
data = lookup.tree("gitorious/mainline", "master", "")
|
|
103
|
+
#=> {
|
|
104
|
+
# :path => "",
|
|
105
|
+
# :ref => "master",
|
|
106
|
+
# :tree => #<Rugged::Tree:16209820 {oid: 89cd7e9d4564928de6b803b36c6e3d081c8d9ca1}>
|
|
107
|
+
# <"README.org" b40c249db94476cac7fa91a9d6491c0faf21ec21>
|
|
108
|
+
# <"lib" 264c348a80906538018616fa16fc35d04bdf38b0>,
|
|
109
|
+
# :readme => { :blob => #<Rugged::Blob:0x00000002111460>, :path => "README.org" }
|
|
110
|
+
# }
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Note that the `tree` lookup will include a readme blob if one is available, and
|
|
114
|
+
Dolt is able to render it.
|
|
115
|
+
|
|
116
|
+
## Controller actions
|
|
117
|
+
|
|
118
|
+
The controller actions that ship with libdolt are web framework agnostic. They
|
|
119
|
+
return arrays that can be passed directly to Rack, or can be picked apart for
|
|
120
|
+
further processing. The controller actions can be configured to redirect any
|
|
121
|
+
request for symbolic refs (e.g. a request for something on "master" will
|
|
122
|
+
redirect to the current tip of that branch), and it provides error handling,
|
|
123
|
+
renders blobs with syntax highlighting and more.
|
|
124
|
+
|
|
125
|
+
The controller actions have three dependencies: a router, a repository lookup
|
|
126
|
+
instance (see above) and a renderer.
|
|
127
|
+
|
|
128
|
+
The router is expected to respond to these messages:
|
|
129
|
+
|
|
130
|
+
* `tree_url(repo, ref, path)`
|
|
131
|
+
* `blob_url(repo, ref, path)`
|
|
132
|
+
* `tree_entry_url(repo, ref, path)`
|
|
133
|
+
* `blame_url(repo, ref, path)`
|
|
134
|
+
* `history_url(repo, ref, path)`
|
|
135
|
+
* `tree_history_url(repo, ref, path)`
|
|
136
|
+
* `raw_url(repo, ref, path)`
|
|
137
|
+
|
|
138
|
+
The renderer can be anything that understands this message:
|
|
139
|
+
|
|
140
|
+
```rb
|
|
141
|
+
renderer.render(template, data, template_options)
|
|
142
|
+
|
|
143
|
+
# e.g.
|
|
144
|
+
renderer.render("tree", {
|
|
145
|
+
:ref => "master",
|
|
146
|
+
:path => lib",
|
|
147
|
+
:tree => tree
|
|
148
|
+
}, { :layout => "dark_skin" })
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
[Tiltout](https://gitorious.org/gitorious/tiltout) is well suited for the task.
|
|
152
|
+
To just use the built-in templates in libdolt:
|
|
153
|
+
|
|
154
|
+
```rb
|
|
155
|
+
renderer = Tiltout.new(Dolt.template_dir, { :layout => "layout" })
|
|
156
|
+
renderer.helper(Dolt::View::Object)
|
|
157
|
+
renderer.helper(Dolt::View::Urls)
|
|
158
|
+
renderer.helper(Dolt::View::Blob)
|
|
159
|
+
renderer.helper(Dolt::View::Blame)
|
|
160
|
+
renderer.helper(Dolt::View::Breadcrumb)
|
|
161
|
+
renderer.helper(Dolt::View::Tree)
|
|
162
|
+
renderer.helper(Dolt::View::Commit)
|
|
163
|
+
renderer.helper(Dolt::View::Gravatar)
|
|
164
|
+
renderer.helper(Dolt::View::TabWidth)
|
|
165
|
+
renderer.helper(Dolt::View::BinaryBlobEmbedder)
|
|
166
|
+
renderer.helper(:tab_width => options[:tab_width], :maxdepth => 3)
|
|
167
|
+
|
|
168
|
+
actions = Dolt::ControllerActions.new(some_router, lookup, renderer)
|
|
169
|
+
response = actions.blob("gitorious/libdolt", "master", "Readme.md")
|
|
170
|
+
|
|
171
|
+
#=> [200, {
|
|
172
|
+
"Content-Type" => "text/html; charset=utf-8",
|
|
173
|
+
"X-UA-Compatible" => "IE=edge"
|
|
174
|
+
}, [html...]]
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
The controller actions also accept a last argument, which is a hash of
|
|
178
|
+
additional data to expose to the templates. This is useful if you are using
|
|
179
|
+
a custom layout and/or templates.
|
|
67
180
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
Then open a browser at [http://localhost:3000](http://localhost:3000). You will
|
|
71
|
-
be redirected to the root tree, and can browse the repository. To view trees and
|
|
72
|
-
blobs at specific refs, use the URL. A branch/tag selector will be added later.
|
|
73
|
-
|
|
74
|
-
## Browsing multiple repositories
|
|
75
|
-
|
|
76
|
-
The idea is that eventually, `dolt` should be able to serve up all Git
|
|
77
|
-
repositories managed by your Gitorious server. It does not yet do that, because
|
|
78
|
-
there currently is no "repository resolver" that understands the hashed paths
|
|
79
|
-
Gitorious uses.
|
|
181
|
+
## Markup rendering
|
|
80
182
|
|
|
81
|
-
|
|
82
|
-
|
|
183
|
+
Dolt uses the [``GitHub::Markup``](https://github.com/github/markup/) library
|
|
184
|
+
(through [Makeup](https://gitorious.org/gitorious/makeup)) to render certain
|
|
185
|
+
markup formats as HTML. Dolt does not have a hard dependency on any of the
|
|
186
|
+
required gems to actually render markups, so see the
|
|
187
|
+
[``GitHub::Markup`` docs](https://github.com/github/markup/) for information on
|
|
188
|
+
what and how to install support for various languages.
|
|
83
189
|
|
|
84
|
-
|
|
190
|
+
Various rendering techniques are implemented as modules that can be included in
|
|
191
|
+
you Tiltout views. Here's an excerpt from Dolt's `bin/dolt` script (which runs a
|
|
192
|
+
standalone repository browser locally on your box):
|
|
85
193
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
194
|
+
```rb
|
|
195
|
+
# Attempt to syntax highlight every blob
|
|
196
|
+
# renderer.helper(Dolt::View::SyntaxHighlight)
|
|
89
197
|
|
|
90
|
-
|
|
198
|
+
# Attempt to render every blob as markup
|
|
199
|
+
# renderer.helper(Dolt::View::Markup)
|
|
91
200
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
201
|
+
# Render supported formats as markup, syntax highlight the rest
|
|
202
|
+
# (if attempting to render some format as markup crashes, it will
|
|
203
|
+
# fall back to syntax highlighting)
|
|
204
|
+
renderer.helper(Dolt::View::SmartBlobRenderer)
|
|
205
|
+
```
|
|
97
206
|
|
|
98
207
|
# License
|
|
99
208
|
|
|
100
|
-
|
|
209
|
+
libdolt is free software licensed under the
|
|
101
210
|
[GNU Affero General Public License (AGPL)](http://www.gnu.org/licenses/agpl-3.0.html).
|
|
102
|
-
|
|
211
|
+
libdolt is developed as part of the Gitorious project.
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
#--
|
|
3
|
+
# Copyright (C) 2012-2013 Gitorious AS
|
|
4
|
+
#
|
|
5
|
+
# This program is free software: you can redistribute it and/or modify
|
|
6
|
+
# it under the terms of the GNU Affero General Public License as published by
|
|
7
|
+
# the Free Software Foundation, either version 3 of the License, or
|
|
8
|
+
# (at your option) any later version.
|
|
9
|
+
#
|
|
10
|
+
# This program is distributed in the hope that it will be useful,
|
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
13
|
+
# GNU Affero General Public License for more details.
|
|
14
|
+
#
|
|
15
|
+
# You should have received a copy of the GNU Affero General Public License
|
|
16
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
17
|
+
#++
|
|
18
|
+
require "json"
|
|
19
|
+
require "time"
|
|
20
|
+
require "cgi"
|
|
21
|
+
|
|
22
|
+
module Dolt
|
|
23
|
+
class ControllerActions
|
|
24
|
+
def initialize(router, lookup, renderer)
|
|
25
|
+
@router = router
|
|
26
|
+
@lookup = lookup
|
|
27
|
+
@renderer = renderer
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def redirect(url, status = 302)
|
|
31
|
+
body = "You are being <a href=\"#{url}\">redirected to #{url}</a>"
|
|
32
|
+
[status, { "Location" => url }, [body]]
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def render_error(error, repo, ref, data = {})
|
|
36
|
+
$stderr.puts(error.message)
|
|
37
|
+
$stderr.puts(error.backtrace)
|
|
38
|
+
|
|
39
|
+
if error.class.to_s == "Rugged::ReferenceError" && ref == "HEAD"
|
|
40
|
+
return [200, headers, [renderer.render("empty", {
|
|
41
|
+
:repository => repo,
|
|
42
|
+
:ref => ref
|
|
43
|
+
}.merge(data))]]
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
[response, headers, [renderer.render(response.to_s.to_sym, {
|
|
47
|
+
:error => error,
|
|
48
|
+
:repository_slug => repo,
|
|
49
|
+
:ref => ref
|
|
50
|
+
}.merge(data))]]
|
|
51
|
+
rescue Exception => err
|
|
52
|
+
err_backtrace = err.backtrace.map { |s| "<li>#{s}</li>" }
|
|
53
|
+
error_backtrace = error.backtrace.map { |s| "<li>#{s}</li>" }
|
|
54
|
+
|
|
55
|
+
[500, headers, [<<-HTML]]
|
|
56
|
+
<h1>Fatal Dolt Error</h1>
|
|
57
|
+
<p>
|
|
58
|
+
Dolt encountered an exception, and additionally
|
|
59
|
+
triggered another exception trying to render the error.
|
|
60
|
+
</p>
|
|
61
|
+
<p>Tried to render the #{template} template with the following data:</p>
|
|
62
|
+
<dl>
|
|
63
|
+
<dt>Repository</dt>
|
|
64
|
+
<dd>#{repo}</dd>
|
|
65
|
+
<dt>Ref</dt>
|
|
66
|
+
<dd>#{ref}</dd>
|
|
67
|
+
</dl>
|
|
68
|
+
<h2>Error: #{err.class} #{err.message}</h2>
|
|
69
|
+
<ul>#{err_backtrace.join()}</ul>
|
|
70
|
+
<h2>Original error: #{error.class} #{error.message}</h2>
|
|
71
|
+
<ul>#{error_backtrace.join()}</ul>
|
|
72
|
+
HTML
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def raw(repo, ref, path, custom_data = {})
|
|
76
|
+
if oid = lookup_ref_oid(repo, ref)
|
|
77
|
+
return redirect(router.raw_url(repo, oid, path), 307)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
blob(repo, ref, path, custom_data, {
|
|
81
|
+
:template => :raw,
|
|
82
|
+
:content_type => "text/plain",
|
|
83
|
+
:template_options => { :layout => nil }
|
|
84
|
+
})
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def blob(repo, ref, path, custom_data = {}, options = { :template => :blob })
|
|
88
|
+
if oid = lookup_ref_oid(repo, ref)
|
|
89
|
+
return redirect(router.blob_url(repo, oid, path), 307)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
data = (custom_data || {}).merge(lookup.blob(repo, u(ref), path))
|
|
93
|
+
blob = data[:blob]
|
|
94
|
+
return redirect(router.tree_url(repo, ref, path)) if blob.class.to_s !~ /\bBlob/
|
|
95
|
+
|
|
96
|
+
tpl_options = options[:template_options] || {}
|
|
97
|
+
[200, headers(options.merge(:ref => ref)), [
|
|
98
|
+
renderer.render(options[:template], data, tpl_options)
|
|
99
|
+
]]
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def tree(repo, ref, path, custom_data = {})
|
|
103
|
+
if oid = lookup_ref_oid(repo, ref)
|
|
104
|
+
return redirect(router.tree_url(repo, oid, path), 307)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
data = (custom_data || {}).merge(lookup.tree(repo, u(ref), path))
|
|
108
|
+
tree = data[:tree]
|
|
109
|
+
return redirect(router.blob_url(repo, ref, path)) if tree.class.to_s !~ /\bTree/
|
|
110
|
+
[200, headers(:ref => ref), [renderer.render(:tree, data)]]
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def tree_entry(repo, ref, path, custom_data = {})
|
|
114
|
+
if oid = lookup_ref_oid(repo, ref)
|
|
115
|
+
return redirect(router.tree_entry_url(repo, oid, path), 307)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
data = (custom_data || {}).merge(lookup.tree_entry(repo, u(ref), path))
|
|
119
|
+
body = renderer.render(data.key?(:tree) ? :tree : :blob, data)
|
|
120
|
+
[200, headers(:ref => ref), [body]]
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def blame(repo, ref, path, custom_data = {})
|
|
124
|
+
if oid = lookup_ref_oid(repo, ref)
|
|
125
|
+
return redirect(router.blame_url(repo, oid, path), 307)
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
data = (custom_data || {}).merge(lookup.blame(repo, u(ref), path))
|
|
129
|
+
[200, headers(:ref => ref), [renderer.render(:blame, data)]]
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def history(repo, ref, path, count, custom_data = {})
|
|
133
|
+
if oid = lookup_ref_oid(repo, ref)
|
|
134
|
+
return redirect(router.history_url(repo, oid, path), 307)
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
data = (custom_data || {}).merge(lookup.history(repo, u(ref), path, count))
|
|
138
|
+
[200, headers(:ref => ref), [renderer.render(:commits, data)]]
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def refs(repo, custom_data = {})
|
|
142
|
+
data = (custom_data || {}).merge(lookup.refs(repo))
|
|
143
|
+
[200, headers(:content_type => "application/json"), [
|
|
144
|
+
renderer.render(:refs, data, :layout => nil)
|
|
145
|
+
]]
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def tree_history(repo, ref, path, count = 1, custom_data = {})
|
|
149
|
+
if oid = lookup_ref_oid(repo, ref)
|
|
150
|
+
return redirect(router.tree_history_url(repo, oid, path), 307)
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
data = (custom_data || {}).merge(lookup.tree_history(repo, u(ref), path, count))
|
|
154
|
+
[200, headers(:content_type => "application/json", :ref => ref), [
|
|
155
|
+
renderer.render(:tree_history, data, :layout => nil)
|
|
156
|
+
]]
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def resolve_repository(repo)
|
|
160
|
+
@cache ||= {}
|
|
161
|
+
@cache[repo] ||= lookup.resolve_repository(repo)
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def lookup_ref_oid(repo, ref)
|
|
165
|
+
return if !router.respond_to?(:redirect_refs?) || !router.redirect_refs? || ref.length == 40
|
|
166
|
+
lookup.rev_parse_oid(repo, ref)
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
private
|
|
170
|
+
attr_reader :router, :lookup, :renderer
|
|
171
|
+
|
|
172
|
+
def u(str)
|
|
173
|
+
# Temporarily swap the + out with a magic byte, so
|
|
174
|
+
# filenames/branches with +'s won't get unescaped to a space
|
|
175
|
+
CGI.unescape(str.gsub("+", "\001")).gsub("\001", '+')
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def headers(options = {})
|
|
179
|
+
default_ct = "text/html; charset=utf-8"
|
|
180
|
+
year = 60*60*24*365
|
|
181
|
+
|
|
182
|
+
{
|
|
183
|
+
"Content-Type" => options[:content_type] || default_ct,
|
|
184
|
+
"X-UA-Compatible" => "IE=edge"
|
|
185
|
+
}.merge(!options[:ref] || options[:ref].length != 40 ? {} : {
|
|
186
|
+
"Cache-Control" => "max-age=315360000, public",
|
|
187
|
+
"Expires" => (Time.now + year).httpdate
|
|
188
|
+
})
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
end
|
data/lib/libdolt/version.rb
CHANGED
data/lib/libdolt.rb
CHANGED
|
@@ -0,0 +1,475 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
#--
|
|
3
|
+
# Copyright (C) 2012-2013 Gitorious AS
|
|
4
|
+
#
|
|
5
|
+
# This program is free software: you can redistribute it and/or modify
|
|
6
|
+
# it under the terms of the GNU Affero General Public License as published by
|
|
7
|
+
# the Free Software Foundation, either version 3 of the License, or
|
|
8
|
+
# (at your option) any later version.
|
|
9
|
+
#
|
|
10
|
+
# This program is distributed in the hope that it will be useful,
|
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
13
|
+
# GNU Affero General Public License for more details.
|
|
14
|
+
#
|
|
15
|
+
# You should have received a copy of the GNU Affero General Public License
|
|
16
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
17
|
+
#++
|
|
18
|
+
require "test_helper"
|
|
19
|
+
require "libdolt/controller_actions"
|
|
20
|
+
|
|
21
|
+
describe Dolt::ControllerActions do
|
|
22
|
+
describe "#blob" do
|
|
23
|
+
it "delegates to lookup" do
|
|
24
|
+
lookup = Test::Lookup.new(Stub::Blob.new)
|
|
25
|
+
dolt = Dolt::ControllerActions.new(Test::Router.new, lookup, Test::Renderer.new)
|
|
26
|
+
|
|
27
|
+
dolt.blob("gitorious", "master", "app/models/repository.rb")
|
|
28
|
+
|
|
29
|
+
assert_equal "gitorious", lookup.repo
|
|
30
|
+
assert_equal "master", lookup.ref
|
|
31
|
+
assert_equal "app/models/repository.rb", lookup.path
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
it "renders the blob template as html" do
|
|
35
|
+
router = Test::Router.new
|
|
36
|
+
dolt = Dolt::ControllerActions.new(router, Test::Lookup.new(Stub::Blob.new), Test::Renderer.new("Blob"))
|
|
37
|
+
|
|
38
|
+
response = dolt.blob("gitorious", "master", "app/models/repository.rb")
|
|
39
|
+
|
|
40
|
+
assert_equal "text/html; charset=utf-8", header(response, "Content-Type")
|
|
41
|
+
assert_equal "blob:Blob", body(response)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
it "renders the blob template with custom data" do
|
|
45
|
+
renderer = Test::Renderer.new("Blob")
|
|
46
|
+
dolt = Dolt::ControllerActions.new(Test::Router.new, Test::Lookup.new(Stub::Blob.new), renderer)
|
|
47
|
+
|
|
48
|
+
dolt.blob("gitorious", "master", "app/models/repository.rb", { :who => 42 })
|
|
49
|
+
|
|
50
|
+
assert_equal 42, renderer.data[:who]
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
it "redirects tree views to tree action" do
|
|
54
|
+
router = Test::Router.new
|
|
55
|
+
dolt = Dolt::ControllerActions.new(router, Test::Lookup.new(Stub::Tree.new), Test::Renderer.new("Tree"))
|
|
56
|
+
|
|
57
|
+
response = dolt.blob("gitorious", "master", "app/models")
|
|
58
|
+
|
|
59
|
+
assert_equal 302, status(response)
|
|
60
|
+
assert_equal "/gitorious/tree/master:app/models", header(response, "Location")
|
|
61
|
+
assert_match "You are being ", body(response)
|
|
62
|
+
assert_match "redirected", body(response)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
it "unescapes ref" do
|
|
66
|
+
lookup = Test::Lookup.new(Stub::Blob.new)
|
|
67
|
+
dolt = Dolt::ControllerActions.new(Test::Router.new, lookup, Test::Renderer.new("Blob"))
|
|
68
|
+
|
|
69
|
+
dolt.blob("gitorious", "issue-%23221", "app/my documents")
|
|
70
|
+
|
|
71
|
+
assert_equal "issue-#221", lookup.ref
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
it "does not redirect ref to oid by default" do
|
|
75
|
+
router = Test::Router.new
|
|
76
|
+
dolt = Dolt::ControllerActions.new(router, Test::Lookup.new(Stub::Blob.new), Test::Renderer.new("Blob"))
|
|
77
|
+
|
|
78
|
+
response = dolt.blob("gitorious", "master", "lib/gitorious.rb")
|
|
79
|
+
|
|
80
|
+
location = header(response, "Location")
|
|
81
|
+
refute_equal 302, status(response)
|
|
82
|
+
refute_equal 307, status(response)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
it "redirects ref to oid if configured so" do
|
|
86
|
+
router = Test::RedirectingRouter.new
|
|
87
|
+
dolt = Dolt::ControllerActions.new(router, Test::Lookup.new(Stub::Blob.new), Test::Renderer.new("Blob"))
|
|
88
|
+
|
|
89
|
+
response = dolt.blob("gitorious", "master", "lib/gitorious.rb")
|
|
90
|
+
|
|
91
|
+
location = header(response, "Location")
|
|
92
|
+
assert_equal 307, status(response)
|
|
93
|
+
assert_equal "/gitorious/blob/#{'a' * 40}:lib/gitorious.rb", location
|
|
94
|
+
assert_match "You are being", body(response)
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
describe "#tree" do
|
|
99
|
+
it "delegates to actions" do
|
|
100
|
+
lookup = Test::Lookup.new(Stub::Tree.new)
|
|
101
|
+
dolt = Dolt::ControllerActions.new(Test::Router.new, lookup, Test::Renderer.new)
|
|
102
|
+
|
|
103
|
+
dolt.tree("gitorious", "master", "app/models")
|
|
104
|
+
|
|
105
|
+
assert_equal "gitorious", lookup.repo
|
|
106
|
+
assert_equal "master", lookup.ref
|
|
107
|
+
assert_equal "app/models", lookup.path
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
it "renders the tree template as html" do
|
|
111
|
+
router = Test::Router.new
|
|
112
|
+
dolt = Dolt::ControllerActions.new(router, Test::Lookup.new(Stub::Tree.new), Test::Renderer.new("Tree"))
|
|
113
|
+
|
|
114
|
+
response = dolt.tree("gitorious", "master", "app/models")
|
|
115
|
+
|
|
116
|
+
assert_equal "text/html; charset=utf-8", header(response, "Content-Type")
|
|
117
|
+
assert_equal "tree:Tree", body(response)
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
it "renders template with custom data" do
|
|
121
|
+
renderer = Test::Renderer.new("Tree")
|
|
122
|
+
dolt = Dolt::ControllerActions.new(Test::Router.new, Test::Lookup.new(Stub::Tree.new), renderer)
|
|
123
|
+
|
|
124
|
+
dolt.tree("gitorious", "master", "app/models", { :who => 42 })
|
|
125
|
+
|
|
126
|
+
assert_equal 42, renderer.data[:who]
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
it "redirects blob views to blob action" do
|
|
130
|
+
router = Test::Router.new
|
|
131
|
+
dolt = Dolt::ControllerActions.new(router, Test::Lookup.new(Stub::Blob.new), Test::Renderer.new("Tree"))
|
|
132
|
+
|
|
133
|
+
response = dolt.tree("gitorious", "master", "app/models/repository.rb")
|
|
134
|
+
|
|
135
|
+
location = header(response, "Location")
|
|
136
|
+
assert_equal 302, status(response)
|
|
137
|
+
assert_equal "/gitorious/blob/master:app/models/repository.rb", location
|
|
138
|
+
assert_match "You are being", body(response)
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
it "sets X-UA-Compatible header" do
|
|
142
|
+
router = Test::Router.new
|
|
143
|
+
dolt = Dolt::ControllerActions.new(router, Test::Lookup.new(Stub::Tree.new), Test::Renderer.new("Tree"))
|
|
144
|
+
|
|
145
|
+
response = dolt.tree("gitorious", "master", "app/models")
|
|
146
|
+
|
|
147
|
+
assert_equal "IE=edge", header(response, "X-UA-Compatible")
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
it "does not set cache-control header for head ref" do
|
|
151
|
+
router = Test::Router.new
|
|
152
|
+
dolt = Dolt::ControllerActions.new(router, Test::Lookup.new(Stub::Tree.new), Test::Renderer.new("Tree"))
|
|
153
|
+
|
|
154
|
+
response = dolt.tree("gitorious", "master", "app/models")
|
|
155
|
+
|
|
156
|
+
assert_nil header(response, "Cache-Control")
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
it "sets cache headers for full oid ref" do
|
|
160
|
+
router = Test::Router.new
|
|
161
|
+
dolt = Dolt::ControllerActions.new(router, Test::Lookup.new(Stub::Tree.new), Test::Renderer.new("Tree"))
|
|
162
|
+
|
|
163
|
+
response = dolt.tree("gitorious", "a" * 40, "app/models")
|
|
164
|
+
|
|
165
|
+
assert_equal "max-age=315360000, public", header(response, "Cache-Control")
|
|
166
|
+
refute_nil header(response, "Expires")
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
it "unescapes ref" do
|
|
170
|
+
lookup = Test::Lookup.new(Stub::Tree.new)
|
|
171
|
+
dolt = Dolt::ControllerActions.new(Test::Router.new, lookup, Test::Renderer.new("Tree"))
|
|
172
|
+
|
|
173
|
+
dolt.tree("gitorious", "issue-%23221", "app")
|
|
174
|
+
|
|
175
|
+
assert_equal "issue-#221", lookup.ref
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
it "redirects ref to oid if configured so" do
|
|
179
|
+
router = Test::RedirectingRouter.new
|
|
180
|
+
dolt = Dolt::ControllerActions.new(router, Test::Lookup.new(Stub::Tree.new), Test::Renderer.new("Tree"))
|
|
181
|
+
|
|
182
|
+
response = dolt.tree("gitorious", "master", "lib")
|
|
183
|
+
|
|
184
|
+
assert_equal 307, status(response)
|
|
185
|
+
assert_equal "/gitorious/tree/#{'a' * 40}:lib", header(response, "Location")
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
describe "#tree_entry" do
|
|
190
|
+
it "renders trees with the tree template as html" do
|
|
191
|
+
router = Test::Router.new
|
|
192
|
+
dolt = Dolt::ControllerActions.new(router, Test::Lookup.new(Stub::Tree.new), Test::Renderer.new("Tree"))
|
|
193
|
+
|
|
194
|
+
response = dolt.tree_entry("gitorious", "master", "app/models")
|
|
195
|
+
|
|
196
|
+
assert_equal "text/html; charset=utf-8", header(response, "Content-Type")
|
|
197
|
+
assert_equal "tree:Tree", body(response)
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
it "renders template with custom data" do
|
|
201
|
+
renderer = Test::Renderer.new("Tree")
|
|
202
|
+
dolt = Dolt::ControllerActions.new(Test::Router.new, Test::Lookup.new(Stub::Tree.new), renderer)
|
|
203
|
+
|
|
204
|
+
dolt.tree_entry("gitorious", "master", "app/models", { :who => 42 })
|
|
205
|
+
|
|
206
|
+
assert_equal 42, renderer.data[:who]
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
it "renders trees with the tree template as html" do
|
|
210
|
+
router = Test::Router.new
|
|
211
|
+
dolt = Dolt::ControllerActions.new(router, Test::Lookup.new(Stub::Blob.new), Test::Renderer.new("Blob"))
|
|
212
|
+
|
|
213
|
+
response = dolt.tree_entry("gitorious", "master", "app/models")
|
|
214
|
+
|
|
215
|
+
assert_equal "text/html; charset=utf-8", header(response, "Content-Type")
|
|
216
|
+
assert_equal "blob:Blob", body(response)
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
it "unescapes ref" do
|
|
220
|
+
lookup = Test::Lookup.new(Stub::Tree.new)
|
|
221
|
+
dolt = Dolt::ControllerActions.new(Test::Router.new, lookup, Test::Renderer.new("Tree"))
|
|
222
|
+
|
|
223
|
+
dolt.tree_entry("gitorious", "issue-%23221", "app")
|
|
224
|
+
|
|
225
|
+
assert_equal "issue-#221", lookup.ref
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
it "redirects ref to oid if configured so" do
|
|
229
|
+
router = Test::RedirectingRouter.new
|
|
230
|
+
dolt = Dolt::ControllerActions.new(router, Test::Lookup.new(Stub::Tree.new), Test::Renderer.new("Tree"))
|
|
231
|
+
|
|
232
|
+
response = dolt.tree_entry("gitorious", "master", "lib")
|
|
233
|
+
|
|
234
|
+
assert_equal 307, status(response)
|
|
235
|
+
assert_equal "/gitorious/source/#{'a' * 40}:lib", header(response, "Location")
|
|
236
|
+
end
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
describe "#raw" do
|
|
240
|
+
it "delegates to lookup" do
|
|
241
|
+
lookup = Test::Lookup.new(Stub::Blob.new)
|
|
242
|
+
dolt = Dolt::ControllerActions.new(Test::Router.new, lookup, Test::Renderer.new)
|
|
243
|
+
|
|
244
|
+
dolt.raw("gitorious", "master", "app/models/repository.rb")
|
|
245
|
+
|
|
246
|
+
assert_equal "gitorious", lookup.repo
|
|
247
|
+
assert_equal "master", lookup.ref
|
|
248
|
+
assert_equal "app/models/repository.rb", lookup.path
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
it "renders the raw template as text" do
|
|
252
|
+
router = Test::Router.new
|
|
253
|
+
dolt = Dolt::ControllerActions.new(router, Test::Lookup.new(Stub::Blob.new), Test::Renderer.new("Text"))
|
|
254
|
+
|
|
255
|
+
response = dolt.raw("gitorious", "master", "app/models/repository.rb")
|
|
256
|
+
|
|
257
|
+
assert_equal "text/plain", header(response, "Content-Type")
|
|
258
|
+
assert_equal "raw:Text", body(response)
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
it "renders template with custom data" do
|
|
262
|
+
renderer = Test::Renderer.new("Text")
|
|
263
|
+
dolt = Dolt::ControllerActions.new(Test::Router.new, Test::Lookup.new(Stub::Blob.new), renderer)
|
|
264
|
+
|
|
265
|
+
dolt.raw("gitorious", "master", "app/models/repository.rb", { :who => 42 })
|
|
266
|
+
|
|
267
|
+
assert_equal 42, renderer.data[:who]
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
it "redirects tree views to tree action" do
|
|
271
|
+
router = Test::Router.new
|
|
272
|
+
dolt = Dolt::ControllerActions.new(router, Test::Lookup.new(Stub::Tree.new), Test::Renderer.new("Tree"))
|
|
273
|
+
|
|
274
|
+
response = dolt.raw("gitorious", "master", "app/models")
|
|
275
|
+
|
|
276
|
+
location = header(response, "Location")
|
|
277
|
+
assert_equal 302, status(response)
|
|
278
|
+
assert_equal "/gitorious/tree/master:app/models", location
|
|
279
|
+
assert_match "You are being", body(response)
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
it "unescapes ref" do
|
|
283
|
+
lookup = Test::Lookup.new(Stub::Blob.new)
|
|
284
|
+
dolt = Dolt::ControllerActions.new(Test::Router.new, lookup, Test::Renderer.new("Blob"))
|
|
285
|
+
|
|
286
|
+
dolt.raw("gitorious", "issue-%23221", "app/models/repository.rb")
|
|
287
|
+
|
|
288
|
+
assert_equal "issue-#221", lookup.ref
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
it "redirects ref to oid if configured so" do
|
|
292
|
+
router = Test::RedirectingRouter.new
|
|
293
|
+
dolt = Dolt::ControllerActions.new(router, Test::Lookup.new(Stub::Blob.new), Test::Renderer.new("Blob"))
|
|
294
|
+
|
|
295
|
+
response = dolt.raw("gitorious", "master", "lib/gitorious.rb")
|
|
296
|
+
|
|
297
|
+
assert_equal 307, status(response)
|
|
298
|
+
assert_equal "/gitorious/raw/#{'a' * 40}:lib/gitorious.rb", header(response, "Location")
|
|
299
|
+
end
|
|
300
|
+
end
|
|
301
|
+
|
|
302
|
+
describe "#blame" do
|
|
303
|
+
it "delegates to lookup" do
|
|
304
|
+
lookup = Test::Lookup.new(Stub::Blob.new)
|
|
305
|
+
dolt = Dolt::ControllerActions.new(Test::Router.new, lookup, Test::Renderer.new)
|
|
306
|
+
|
|
307
|
+
dolt.blame("gitorious", "master", "app/models/repository.rb")
|
|
308
|
+
|
|
309
|
+
assert_equal "gitorious", lookup.repo
|
|
310
|
+
assert_equal "master", lookup.ref
|
|
311
|
+
assert_equal "app/models/repository.rb", lookup.path
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
it "renders the blame template as html" do
|
|
315
|
+
router = Test::Router.new
|
|
316
|
+
dolt = Dolt::ControllerActions.new(router, Test::Lookup.new(Stub::Blob.new), Test::Renderer.new("Text"))
|
|
317
|
+
|
|
318
|
+
response = dolt.blame("gitorious", "master", "app/models/repository.rb")
|
|
319
|
+
|
|
320
|
+
assert_equal "text/html; charset=utf-8", header(response, "Content-Type")
|
|
321
|
+
assert_equal "blame:Text", body(response)
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
it "renders template with custom data" do
|
|
325
|
+
renderer = Test::Renderer.new("Text")
|
|
326
|
+
dolt = Dolt::ControllerActions.new(Test::Router.new, Test::Lookup.new(Stub::Blob.new), renderer)
|
|
327
|
+
|
|
328
|
+
dolt.blame("gitorious", "master", "app/models/repository.rb", { :who => 42 })
|
|
329
|
+
|
|
330
|
+
assert_equal 42, renderer.data[:who]
|
|
331
|
+
end
|
|
332
|
+
|
|
333
|
+
it "unescapes ref" do
|
|
334
|
+
lookup = Test::Lookup.new(Stub::Blob.new)
|
|
335
|
+
dolt = Dolt::ControllerActions.new(Test::Router.new, lookup, Test::Renderer.new("Blob"))
|
|
336
|
+
|
|
337
|
+
dolt.blame("gitorious", "issue-%23221", "app/models/repository.rb")
|
|
338
|
+
|
|
339
|
+
assert_equal "issue-#221", lookup.ref
|
|
340
|
+
end
|
|
341
|
+
|
|
342
|
+
it "redirects ref to oid if configured so" do
|
|
343
|
+
router = Test::RedirectingRouter.new
|
|
344
|
+
dolt = Dolt::ControllerActions.new(router, Test::Lookup.new(Stub::Blob.new), Test::Renderer.new("Blob"))
|
|
345
|
+
|
|
346
|
+
response = dolt.blame("gitorious", "master", "lib/gitorious.rb")
|
|
347
|
+
|
|
348
|
+
assert_equal 307, status(response)
|
|
349
|
+
assert_equal "/gitorious/blame/#{'a' * 40}:lib/gitorious.rb", header(response, "Location")
|
|
350
|
+
end
|
|
351
|
+
end
|
|
352
|
+
|
|
353
|
+
describe "#history" do
|
|
354
|
+
it "delegates to lookup" do
|
|
355
|
+
lookup = Test::Lookup.new(Stub::Blob.new)
|
|
356
|
+
dolt = Dolt::ControllerActions.new(Test::Router.new, lookup, Test::Renderer.new)
|
|
357
|
+
dolt.history("gitorious", "master", "app/models/repository.rb", 10)
|
|
358
|
+
|
|
359
|
+
assert_equal "gitorious", lookup.repo
|
|
360
|
+
assert_equal "master", lookup.ref
|
|
361
|
+
assert_equal "app/models/repository.rb", lookup.path
|
|
362
|
+
end
|
|
363
|
+
|
|
364
|
+
it "renders the commits template as html" do
|
|
365
|
+
router = Test::Router.new
|
|
366
|
+
dolt = Dolt::ControllerActions.new(router, Test::Lookup.new(Stub::Blob.new), Test::Renderer.new("Text"))
|
|
367
|
+
|
|
368
|
+
response = dolt.history("gitorious", "master", "app/models/repository.rb", 10)
|
|
369
|
+
|
|
370
|
+
assert_equal "text/html; charset=utf-8", header(response, "Content-Type")
|
|
371
|
+
assert_equal "commits:Text", body(response)
|
|
372
|
+
end
|
|
373
|
+
|
|
374
|
+
it "renders template with custom data" do
|
|
375
|
+
renderer = Test::Renderer.new("Text")
|
|
376
|
+
dolt = Dolt::ControllerActions.new(Test::Router.new, Test::Lookup.new(Stub::Blob.new), renderer)
|
|
377
|
+
|
|
378
|
+
dolt.history("gitorious", "master", "app/models/repository.rb", 10, { :who => 42 })
|
|
379
|
+
|
|
380
|
+
assert_equal 42, renderer.data[:who]
|
|
381
|
+
end
|
|
382
|
+
|
|
383
|
+
it "unescapes ref" do
|
|
384
|
+
lookup = Test::Lookup.new(Stub::Blob.new)
|
|
385
|
+
dolt = Dolt::ControllerActions.new(Test::Router.new, lookup, Test::Renderer.new("Blob"))
|
|
386
|
+
|
|
387
|
+
dolt.history("gitorious", "issue-%23221", "lib/gitorious.rb", 10)
|
|
388
|
+
|
|
389
|
+
assert_equal "issue-#221", lookup.ref
|
|
390
|
+
end
|
|
391
|
+
|
|
392
|
+
it "redirects ref to oid if configured so" do
|
|
393
|
+
router = Test::RedirectingRouter.new
|
|
394
|
+
dolt = Dolt::ControllerActions.new(router, Test::Lookup.new(Stub::Blob.new), Test::Renderer.new("Blob"))
|
|
395
|
+
|
|
396
|
+
response = dolt.history("gitorious", "master", "lib/gitorious.rb", 10)
|
|
397
|
+
|
|
398
|
+
assert_equal 307, status(response)
|
|
399
|
+
assert_equal "/gitorious/history/#{'a' * 40}:lib/gitorious.rb", header(response, "Location")
|
|
400
|
+
end
|
|
401
|
+
end
|
|
402
|
+
|
|
403
|
+
describe "#refs" do
|
|
404
|
+
it "renders the refs template as json" do
|
|
405
|
+
router = Test::Router.new
|
|
406
|
+
dolt = Dolt::ControllerActions.new(router, Test::Lookup.new(Stub::Blob.new), Test::Renderer.new("JSON"))
|
|
407
|
+
|
|
408
|
+
response = dolt.refs("gitorious")
|
|
409
|
+
|
|
410
|
+
assert_equal "application/json", header(response, "Content-Type")
|
|
411
|
+
assert_equal "refs:JSON", body(response)
|
|
412
|
+
end
|
|
413
|
+
|
|
414
|
+
it "renders template with custom data" do
|
|
415
|
+
renderer = Test::Renderer.new("Text")
|
|
416
|
+
dolt = Dolt::ControllerActions.new(Test::Router.new, Test::Lookup.new(Stub::Blob.new), renderer)
|
|
417
|
+
|
|
418
|
+
dolt.refs("gitorious", { :who => 42 })
|
|
419
|
+
|
|
420
|
+
assert_equal 42, renderer.data[:who]
|
|
421
|
+
end
|
|
422
|
+
end
|
|
423
|
+
|
|
424
|
+
describe "#tree_history" do
|
|
425
|
+
it "renders the tree_history template as json" do
|
|
426
|
+
router = Test::Router.new
|
|
427
|
+
dolt = Dolt::ControllerActions.new(router, Test::Lookup.new(Stub::Tree.new), Test::Renderer.new("JSON"))
|
|
428
|
+
|
|
429
|
+
response = dolt.tree_history("gitorious", "master", "", 1)
|
|
430
|
+
|
|
431
|
+
assert_equal "application/json", header(response, "Content-Type")
|
|
432
|
+
assert_equal "tree_history:JSON", body(response)
|
|
433
|
+
end
|
|
434
|
+
|
|
435
|
+
it "renders template with custom data" do
|
|
436
|
+
renderer = Test::Renderer.new("Text")
|
|
437
|
+
dolt = Dolt::ControllerActions.new(Test::Router.new, Test::Lookup.new(Stub::Tree.new), renderer)
|
|
438
|
+
|
|
439
|
+
dolt.tree_history("gitorious", "master", "app/models", 1, { :who => 42 })
|
|
440
|
+
|
|
441
|
+
assert_equal 42, renderer.data[:who]
|
|
442
|
+
end
|
|
443
|
+
|
|
444
|
+
it "unescapes ref" do
|
|
445
|
+
lookup = Test::Lookup.new(Stub::Tree.new)
|
|
446
|
+
dolt = Dolt::ControllerActions.new(Test::Router.new, lookup, Test::Renderer.new("Tree"))
|
|
447
|
+
|
|
448
|
+
dolt.tree_history("gitorious", "issue-%23221", "app/models")
|
|
449
|
+
|
|
450
|
+
assert_equal "issue-#221", lookup.ref
|
|
451
|
+
end
|
|
452
|
+
|
|
453
|
+
it "redirects ref to oid if configured so" do
|
|
454
|
+
router = Test::RedirectingRouter.new
|
|
455
|
+
dolt = Dolt::ControllerActions.new(router, Test::Lookup.new(Stub::Tree.new), Test::Renderer.new("Tree"))
|
|
456
|
+
|
|
457
|
+
response = dolt.tree_history("gitorious", "master", "lib", 10)
|
|
458
|
+
|
|
459
|
+
assert_equal 307, status(response)
|
|
460
|
+
assert_equal "/gitorious/tree_history/#{'a' * 40}:lib", header(response, "Location")
|
|
461
|
+
end
|
|
462
|
+
end
|
|
463
|
+
|
|
464
|
+
def status(response)
|
|
465
|
+
response[0]
|
|
466
|
+
end
|
|
467
|
+
|
|
468
|
+
def header(response, name)
|
|
469
|
+
response[1][name]
|
|
470
|
+
end
|
|
471
|
+
|
|
472
|
+
def body(response)
|
|
473
|
+
response[2].join
|
|
474
|
+
end
|
|
475
|
+
end
|
data/test/test_helper.rb
CHANGED
|
@@ -46,15 +46,15 @@ module Dolt
|
|
|
46
46
|
root = File.join(File.dirname(__FILE__), "..", "views")
|
|
47
47
|
renderer = Tiltout.new(root, options)
|
|
48
48
|
renderer.helper(helpers || [Dolt::View::MultiRepository,
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
49
|
+
Dolt::View::Urls,
|
|
50
|
+
Dolt::View::Object,
|
|
51
|
+
Dolt::View::Blob,
|
|
52
|
+
Dolt::View::Tree,
|
|
53
|
+
Dolt::View::Blame,
|
|
54
|
+
Dolt::View::SyntaxHighlight,
|
|
55
|
+
Dolt::View::Commit,
|
|
56
|
+
Dolt::View::Gravatar,
|
|
57
|
+
Dolt::View::Breadcrumb])
|
|
58
58
|
renderer
|
|
59
59
|
end
|
|
60
60
|
end
|
|
@@ -80,3 +80,121 @@ module Dolt
|
|
|
80
80
|
end
|
|
81
81
|
end
|
|
82
82
|
end
|
|
83
|
+
|
|
84
|
+
module Stub
|
|
85
|
+
class Blob
|
|
86
|
+
def is_a?(type)
|
|
87
|
+
type == Rugged::Blob
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
class Tree
|
|
92
|
+
def is_a?(type)
|
|
93
|
+
type == Rugged::Tree
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
module Test
|
|
99
|
+
class Router
|
|
100
|
+
def tree_url(repo, ref, path)
|
|
101
|
+
"/#{repo}/tree/#{ref}:#{path}"
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def blob_url(repo, ref, path)
|
|
105
|
+
"/#{repo}/blob/#{ref}:#{path}"
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def tree_entry_url(repo, ref, path)
|
|
109
|
+
"/#{repo}/source/#{ref}:#{path}"
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def blame_url(repo, ref, path)
|
|
113
|
+
"/#{repo}/blame/#{ref}:#{path}"
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def history_url(repo, ref, path)
|
|
117
|
+
"/#{repo}/history/#{ref}:#{path}"
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def tree_history_url(repo, ref, path)
|
|
121
|
+
"/#{repo}/tree_history/#{ref}:#{path}"
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def raw_url(repo, ref, path)
|
|
125
|
+
"/#{repo}/raw/#{ref}:#{path}"
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def method_missing(name, *args, &block)
|
|
129
|
+
@actions.send(name, *args, &block)
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
class RedirectingRouter < Router
|
|
134
|
+
def redirect_refs?; true; end
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
class Renderer
|
|
138
|
+
attr_reader :data
|
|
139
|
+
def initialize(body = ""); @body = body; end
|
|
140
|
+
|
|
141
|
+
def render(action, data, options = {})
|
|
142
|
+
@action = action
|
|
143
|
+
@data = data
|
|
144
|
+
"#{action}:#@body"
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
class Lookup
|
|
149
|
+
attr_reader :repo, :ref, :path
|
|
150
|
+
|
|
151
|
+
def initialize(response)
|
|
152
|
+
@response = response
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def blob(repo, ref, path)
|
|
156
|
+
respond(:blob, repo, ref, path)
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def tree(repo, ref, path)
|
|
160
|
+
respond(:tree, repo, ref, path)
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def tree_entry(repo, ref, path)
|
|
164
|
+
respond(:tree_entry, repo, ref, path)
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def raw(repo, ref, path)
|
|
168
|
+
respond(:raw, repo, ref, path)
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def blame(repo, ref, path)
|
|
172
|
+
respond(:blame, repo, ref, path)
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def history(repo, ref, path, limit)
|
|
176
|
+
respond(:history, repo, ref, path)
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
def refs(repo)
|
|
180
|
+
respond(:refs, repo)
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def tree_history(repo, ref, path, count)
|
|
184
|
+
respond(:tree_history, repo, ref, path)
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def respond(type, repo, ref = nil, path = nil)
|
|
188
|
+
@repo = repo
|
|
189
|
+
@ref = ref
|
|
190
|
+
@path = path
|
|
191
|
+
data = { :ref => ref, :repository => repo }
|
|
192
|
+
data[type != :tree_entry ? type : (@response.class.to_s =~ /Tree/ ? :tree : :blob)] = @response
|
|
193
|
+
data
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
def rev_parse_oid(repo, ref)
|
|
197
|
+
"a" * 40
|
|
198
|
+
end
|
|
199
|
+
end
|
|
200
|
+
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: libdolt
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.27.0
|
|
5
5
|
prerelease:
|
|
6
6
|
platform: ruby
|
|
7
7
|
authors:
|
|
@@ -9,7 +9,7 @@ authors:
|
|
|
9
9
|
autorequire:
|
|
10
10
|
bindir: bin
|
|
11
11
|
cert_chain: []
|
|
12
|
-
date: 2013-07-
|
|
12
|
+
date: 2013-07-16 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
|
14
14
|
- !ruby/object:Gem::Dependency
|
|
15
15
|
name: rugged
|
|
@@ -201,6 +201,7 @@ files:
|
|
|
201
201
|
- Rakefile
|
|
202
202
|
- Readme.md
|
|
203
203
|
- lib/libdolt.rb
|
|
204
|
+
- lib/libdolt/controller_actions.rb
|
|
204
205
|
- lib/libdolt/disk_repo_resolver.rb
|
|
205
206
|
- lib/libdolt/git.rb
|
|
206
207
|
- lib/libdolt/git/archiver.rb
|
|
@@ -210,7 +211,6 @@ files:
|
|
|
210
211
|
- lib/libdolt/git/repository.rb
|
|
211
212
|
- lib/libdolt/git/submodule.rb
|
|
212
213
|
- lib/libdolt/git/tree.rb
|
|
213
|
-
- lib/libdolt/gitorious_repo_resolver.rb
|
|
214
214
|
- lib/libdolt/repository_lookup.rb
|
|
215
215
|
- lib/libdolt/version.rb
|
|
216
216
|
- lib/libdolt/view.rb
|
|
@@ -249,6 +249,7 @@ files:
|
|
|
249
249
|
- test/fixtures/dolt-test-repo.git/objects/fc/5f5fb50b435e183925b341909610aace90a413
|
|
250
250
|
- test/fixtures/dolt-test-repo.git/refs/heads/master
|
|
251
251
|
- test/fixtures/dolt-test-repo.git/refs/tags/testable-tag
|
|
252
|
+
- test/libdolt/controller_actions_test.rb
|
|
252
253
|
- test/libdolt/disk_repo_resolver_test.rb
|
|
253
254
|
- test/libdolt/git/archiver_test.rb
|
|
254
255
|
- test/libdolt/git/blame_test.rb
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
# encoding: utf-8
|
|
2
|
-
#--
|
|
3
|
-
# Copyright (C) 2012 Gitorious AS
|
|
4
|
-
#
|
|
5
|
-
# This program is free software: you can redistribute it and/or modify
|
|
6
|
-
# it under the terms of the GNU Affero General Public License as published by
|
|
7
|
-
# the Free Software Foundation, either version 3 of the License, or
|
|
8
|
-
# (at your option) any later version.
|
|
9
|
-
#
|
|
10
|
-
# This program is distributed in the hope that it will be useful,
|
|
11
|
-
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
-
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
13
|
-
# GNU Affero General Public License for more details.
|
|
14
|
-
#
|
|
15
|
-
# You should have received a copy of the GNU Affero General Public License
|
|
16
|
-
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
17
|
-
#++
|
|
18
|
-
|
|
19
|
-
module Dolt
|
|
20
|
-
class GitoriousRepoResolver
|
|
21
|
-
def initialize(repository)
|
|
22
|
-
@repository = repository
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
def resolve(repo = nil)
|
|
26
|
-
Dolt::Git::Repository.new(@repository.full_repository_path)
|
|
27
|
-
end
|
|
28
|
-
end
|
|
29
|
-
end
|