read-only-gollum 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (176) hide show
  1. data/Gemfile +4 -0
  2. data/HISTORY.md +102 -0
  3. data/Home.md +3 -0
  4. data/LICENSE +21 -0
  5. data/README.md +477 -0
  6. data/Rakefile +142 -0
  7. data/bin/read-only-gollum +126 -0
  8. data/docs/sanitization.md +32 -0
  9. data/lib/gollum.rb +41 -0
  10. data/lib/gollum/blob_entry.rb +78 -0
  11. data/lib/gollum/committer.rb +218 -0
  12. data/lib/gollum/file.rb +64 -0
  13. data/lib/gollum/frontend/app.rb +225 -0
  14. data/lib/gollum/frontend/public/css/dialog.css +141 -0
  15. data/lib/gollum/frontend/public/css/editor.css +537 -0
  16. data/lib/gollum/frontend/public/css/gollum.css +660 -0
  17. data/lib/gollum/frontend/public/css/ie7.css +69 -0
  18. data/lib/gollum/frontend/public/css/template.css +381 -0
  19. data/lib/gollum/frontend/public/images/icon-sprite.png +0 -0
  20. data/lib/gollum/frontend/public/javascript/editor/gollum.editor.js +1096 -0
  21. data/lib/gollum/frontend/public/javascript/editor/langs/asciidoc.js +167 -0
  22. data/lib/gollum/frontend/public/javascript/editor/langs/creole.js +104 -0
  23. data/lib/gollum/frontend/public/javascript/editor/langs/markdown.js +211 -0
  24. data/lib/gollum/frontend/public/javascript/editor/langs/org.js +173 -0
  25. data/lib/gollum/frontend/public/javascript/editor/langs/pod.js +111 -0
  26. data/lib/gollum/frontend/public/javascript/editor/langs/rdoc.js +74 -0
  27. data/lib/gollum/frontend/public/javascript/editor/langs/textile.js +175 -0
  28. data/lib/gollum/frontend/public/javascript/gollum.dialog.js +263 -0
  29. data/lib/gollum/frontend/public/javascript/gollum.js +161 -0
  30. data/lib/gollum/frontend/public/javascript/gollum.placeholder.js +54 -0
  31. data/lib/gollum/frontend/public/javascript/jquery.color.js +123 -0
  32. data/lib/gollum/frontend/public/javascript/jquery.js +7179 -0
  33. data/lib/gollum/frontend/templates/compare.mustache +38 -0
  34. data/lib/gollum/frontend/templates/create.mustache +17 -0
  35. data/lib/gollum/frontend/templates/edit.mustache +17 -0
  36. data/lib/gollum/frontend/templates/editor.mustache +116 -0
  37. data/lib/gollum/frontend/templates/error.mustache +8 -0
  38. data/lib/gollum/frontend/templates/history.mustache +58 -0
  39. data/lib/gollum/frontend/templates/layout.mustache +28 -0
  40. data/lib/gollum/frontend/templates/page.mustache +37 -0
  41. data/lib/gollum/frontend/templates/pages.mustache +35 -0
  42. data/lib/gollum/frontend/templates/search.mustache +36 -0
  43. data/lib/gollum/frontend/templates/searchbar.mustache +10 -0
  44. data/lib/gollum/frontend/views/compare.rb +94 -0
  45. data/lib/gollum/frontend/views/create.rb +48 -0
  46. data/lib/gollum/frontend/views/edit.rb +52 -0
  47. data/lib/gollum/frontend/views/editable.rb +13 -0
  48. data/lib/gollum/frontend/views/error.rb +7 -0
  49. data/lib/gollum/frontend/views/history.rb +44 -0
  50. data/lib/gollum/frontend/views/layout.rb +20 -0
  51. data/lib/gollum/frontend/views/page.rb +57 -0
  52. data/lib/gollum/frontend/views/pages.rb +19 -0
  53. data/lib/gollum/frontend/views/search.rb +20 -0
  54. data/lib/gollum/git_access.rb +248 -0
  55. data/lib/gollum/markup.rb +489 -0
  56. data/lib/gollum/page.rb +430 -0
  57. data/lib/gollum/pagination.rb +61 -0
  58. data/lib/gollum/sanitization.rb +174 -0
  59. data/lib/gollum/tex.rb +89 -0
  60. data/lib/gollum/web_sequence_diagram.rb +43 -0
  61. data/lib/gollum/wiki.rb +636 -0
  62. data/read-only-gollum.gemspec +224 -0
  63. data/templates/formatting.html +92 -0
  64. data/test/examples/empty.git/HEAD +1 -0
  65. data/test/examples/empty.git/config +5 -0
  66. data/test/examples/empty.git/description +1 -0
  67. data/test/examples/empty.git/hooks/applypatch-msg.sample +15 -0
  68. data/test/examples/empty.git/hooks/commit-msg.sample +24 -0
  69. data/test/examples/empty.git/hooks/post-commit.sample +8 -0
  70. data/test/examples/empty.git/hooks/post-receive.sample +15 -0
  71. data/test/examples/empty.git/hooks/post-update.sample +8 -0
  72. data/test/examples/empty.git/hooks/pre-applypatch.sample +14 -0
  73. data/test/examples/empty.git/hooks/pre-commit.sample +46 -0
  74. data/test/examples/empty.git/hooks/pre-rebase.sample +169 -0
  75. data/test/examples/empty.git/hooks/prepare-commit-msg.sample +36 -0
  76. data/test/examples/empty.git/hooks/update.sample +128 -0
  77. data/test/examples/empty.git/info/exclude +6 -0
  78. data/test/examples/empty.git/objects/info/.gitkeep +0 -0
  79. data/test/examples/empty.git/objects/pack/.gitkeep +0 -0
  80. data/test/examples/empty.git/refs/heads/.gitkeep +0 -0
  81. data/test/examples/lotr.git/COMMIT_EDITMSG +1 -0
  82. data/test/examples/lotr.git/HEAD +1 -0
  83. data/test/examples/lotr.git/ORIG_HEAD +1 -0
  84. data/test/examples/lotr.git/config +12 -0
  85. data/test/examples/lotr.git/description +1 -0
  86. data/test/examples/lotr.git/index +0 -0
  87. data/test/examples/lotr.git/info/exclude +6 -0
  88. data/test/examples/lotr.git/logs/HEAD +3 -0
  89. data/test/examples/lotr.git/logs/refs/heads/master +3 -0
  90. data/test/examples/lotr.git/objects/06/131480411710c92a82fe2d1e76932c70feb2e5 +0 -0
  91. data/test/examples/lotr.git/objects/0a/de1e2916346d4c1f2fb63b863fd3c16808fe44 +0 -0
  92. data/test/examples/lotr.git/objects/0e/d8cbe0a25235bd867e65193c7d837c66b328ef +3 -0
  93. data/test/examples/lotr.git/objects/12/629d666c5e3178f82f533f543d61b53dc78c0b +0 -0
  94. data/test/examples/lotr.git/objects/1d/b89ebba7e2c14d93b94ff98cfa3708a4f0d4e3 +2 -0
  95. data/test/examples/lotr.git/objects/24/49c2681badfd3c189e8ed658dacffe8ba48fe5 +0 -0
  96. data/test/examples/lotr.git/objects/25/4bdc1ba27d8b8a794538a8522d9a2b56ec2dd9 +0 -0
  97. data/test/examples/lotr.git/objects/2c/b9156ad383914561a8502fc70f5a1d887e48ad +4 -0
  98. data/test/examples/lotr.git/objects/5d/cac289a8603188d2c5caf481dcba2985126aaa +0 -0
  99. data/test/examples/lotr.git/objects/60/f12f4254f58801b9ee7db7bca5fa8aeefaa56b +0 -0
  100. data/test/examples/lotr.git/objects/71/4323c104239440a5c66ab12a67ed07a83c404f +0 -0
  101. data/test/examples/lotr.git/objects/84/0ec5b1ba1320e8ec443f28f99566f615d5af10 +0 -0
  102. data/test/examples/lotr.git/objects/93/6b83ee0dd8837adb82511e40d5e4ebe59bb675 +0 -0
  103. data/test/examples/lotr.git/objects/94/523d7ae48aeba575099dd12926420d8fd0425d +2 -0
  104. data/test/examples/lotr.git/objects/96/97dc65e095658bbd1b8e8678e08881e86d32f1 +0 -0
  105. data/test/examples/lotr.git/objects/a3/1ca2a7c352c92531a8b99815d15843b259e814 +0 -0
  106. data/test/examples/lotr.git/objects/a6/59b3763b822dd97544621fd0beef162ea37b14 +4 -0
  107. data/test/examples/lotr.git/objects/a8/ad3c09dd842a3517085bfadd37718856dee813 +0 -0
  108. data/test/examples/lotr.git/objects/aa/b61fe89d56f8614c0a8151da34f939dcedfa68 +0 -0
  109. data/test/examples/lotr.git/objects/bc/4b5fc0ce2c2ba3acef6647e4f67256ee45ab60 +0 -0
  110. data/test/examples/lotr.git/objects/c3/b43e9f08966b088e7a0192e436b7a884542e05 +0 -0
  111. data/test/examples/lotr.git/objects/dc/596d6b2dd89ab05c66f4abd7d5eb706bc17f19 +0 -0
  112. data/test/examples/lotr.git/objects/ec/da3205bee14520aab5a7bb307392064b938e83 +0 -0
  113. data/test/examples/lotr.git/objects/f4/84ebb1f40f8eb20d1bcd8d1d71934d2b8ae961 +0 -0
  114. data/test/examples/lotr.git/objects/fa/e7ef5344202bba4129abdc13060d9297d99465 +3 -0
  115. data/test/examples/lotr.git/objects/info/packs +2 -0
  116. data/test/examples/lotr.git/objects/pack/pack-dcbeaf3f6ff6c5eb08ea2b0a2d83626e8763546b.idx +0 -0
  117. data/test/examples/lotr.git/objects/pack/pack-dcbeaf3f6ff6c5eb08ea2b0a2d83626e8763546b.pack +0 -0
  118. data/test/examples/lotr.git/packed-refs +2 -0
  119. data/test/examples/lotr.git/refs/heads/master +1 -0
  120. data/test/examples/lotr.git/refs/remotes/origin/HEAD +1 -0
  121. data/test/examples/page_file_dir.git/COMMIT_EDITMSG +1 -0
  122. data/test/examples/page_file_dir.git/HEAD +1 -0
  123. data/test/examples/page_file_dir.git/config +6 -0
  124. data/test/examples/page_file_dir.git/description +1 -0
  125. data/test/examples/page_file_dir.git/index +0 -0
  126. data/test/examples/page_file_dir.git/info/exclude +6 -0
  127. data/test/examples/page_file_dir.git/logs/HEAD +1 -0
  128. data/test/examples/page_file_dir.git/logs/refs/heads/master +1 -0
  129. data/test/examples/page_file_dir.git/objects/0c/7d27db1f575263efdcab3dc650f4502a2dbcbf +0 -0
  130. data/test/examples/page_file_dir.git/objects/22/b404803c966dd92865614d86ff22ca12e50c1e +0 -0
  131. data/test/examples/page_file_dir.git/objects/25/7cc5642cb1a054f08cc83f2d943e56fd3ebe99 +0 -0
  132. data/test/examples/page_file_dir.git/objects/57/16ca5987cbf97d6bb54920bea6adde242d87e6 +0 -0
  133. data/test/examples/page_file_dir.git/objects/5b/43e14e0a15fb6f08feab1773d1c0991e9f71e2 +0 -0
  134. data/test/examples/page_file_dir.git/refs/heads/master +1 -0
  135. data/test/examples/revert.git/COMMIT_EDITMSG +1 -0
  136. data/test/examples/revert.git/HEAD +1 -0
  137. data/test/examples/revert.git/config +12 -0
  138. data/test/examples/revert.git/description +1 -0
  139. data/test/examples/revert.git/index +0 -0
  140. data/test/examples/revert.git/info/exclude +6 -0
  141. data/test/examples/revert.git/logs/HEAD +2 -0
  142. data/test/examples/revert.git/logs/refs/heads/master +2 -0
  143. data/test/examples/revert.git/objects/20/2ced67cea93c7b6bd2928aa1daef8d1d55a20d +0 -0
  144. data/test/examples/revert.git/objects/41/76394bfa11222363c66ce7e84b5f154095b6d9 +0 -0
  145. data/test/examples/revert.git/objects/6a/69f92020f5df77af6e8813ff1232493383b708 +0 -0
  146. data/test/examples/revert.git/objects/b4/785957bc986dc39c629de9fac9df46972c00fc +0 -0
  147. data/test/examples/revert.git/objects/f4/03b791119f8232b7cb0ba455c624ac6435f433 +0 -0
  148. data/test/examples/revert.git/objects/info/packs +2 -0
  149. data/test/examples/revert.git/objects/pack/pack-a561f8437234f74d0bacb9e0eebe52d207f5770d.idx +0 -0
  150. data/test/examples/revert.git/objects/pack/pack-a561f8437234f74d0bacb9e0eebe52d207f5770d.pack +0 -0
  151. data/test/examples/revert.git/packed-refs +2 -0
  152. data/test/examples/revert.git/refs/heads/master +1 -0
  153. data/test/examples/revert.git/refs/remotes/origin/HEAD +1 -0
  154. data/test/examples/yubiwa.git/HEAD +1 -0
  155. data/test/examples/yubiwa.git/config +5 -0
  156. data/test/examples/yubiwa.git/description +1 -0
  157. data/test/examples/yubiwa.git/info/exclude +6 -0
  158. data/test/examples/yubiwa.git/objects/10/fa2ddc4e3b4009d8a453aace10bd6148c1ad00 +0 -0
  159. data/test/examples/yubiwa.git/objects/52/4b82874327ea7cbf730389964ba7cb3de966de +0 -0
  160. data/test/examples/yubiwa.git/objects/58/3fc201cb457fb3f1480f3e1e5999b119633835 +0 -0
  161. data/test/examples/yubiwa.git/objects/87/bc1dd46ab3d3874d4e898d45dd512cc20a7cc8 +1 -0
  162. data/test/examples/yubiwa.git/objects/89/64ed1b4e21aa90e831763bbce9034bfda81b70 +0 -0
  163. data/test/examples/yubiwa.git/objects/9f/f6dd0660da5fba2d3374adb2b84fa653bb538b +0 -0
  164. data/test/examples/yubiwa.git/objects/ac/e97abf2b177815a1972d7db22f229f58c83309 +0 -0
  165. data/test/examples/yubiwa.git/objects/b1/f443863a4816628807fbf86141ebef055dda34 +0 -0
  166. data/test/examples/yubiwa.git/refs/heads/master +1 -0
  167. data/test/helper.rb +66 -0
  168. data/test/test_app.rb +169 -0
  169. data/test/test_committer.rb +64 -0
  170. data/test/test_file.rb +27 -0
  171. data/test/test_git_access.rb +52 -0
  172. data/test/test_markup.rb +628 -0
  173. data/test/test_page.rb +166 -0
  174. data/test/test_page_revert.rb +45 -0
  175. data/test/test_wiki.rb +462 -0
  176. metadata +470 -0
@@ -0,0 +1,48 @@
1
+ module Precious
2
+ module Views
3
+ class Create < Layout
4
+ include Editable
5
+
6
+ attr_reader :page, :name
7
+
8
+ def title
9
+ "Create a new page"
10
+ end
11
+
12
+ def is_create_page
13
+ true
14
+ end
15
+
16
+ def is_edit_page
17
+ false
18
+ end
19
+
20
+ def format
21
+ @format = (@page.format || false) if @format.nil? && @page
22
+ @format.to_s.downcase
23
+ end
24
+
25
+ def has_footer
26
+ @footer = (@page.footer || false) if @footer.nil? && @page
27
+ !!@footer
28
+ end
29
+
30
+ def has_sidebar
31
+ @sidebar = (@page.sidebar || false) if @sidebar.nil? && @page
32
+ !!@sidebar
33
+ end
34
+
35
+ def page_name
36
+ @name.gsub('-', ' ')
37
+ end
38
+
39
+ def formats
40
+ super(:markdown)
41
+ end
42
+
43
+ def default_markup
44
+ Precious::App.settings.default_markup
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,52 @@
1
+ module Precious
2
+ module Views
3
+ class Edit < Layout
4
+ include Editable
5
+
6
+ attr_reader :page, :content
7
+
8
+ def title
9
+ "#{@page.title}"
10
+ end
11
+
12
+ def page_name
13
+ @name.gsub('-', ' ')
14
+ end
15
+
16
+ def footer
17
+ if @footer.nil?
18
+ if page = @page.footer
19
+ @footer = page.raw_data
20
+ else
21
+ @footer = false
22
+ end
23
+ end
24
+ @footer
25
+ end
26
+
27
+ def sidebar
28
+ if @sidebar.nil?
29
+ if page = @page.sidebar
30
+ @sidebar = page.raw_data
31
+ else
32
+ @sidebar = false
33
+ end
34
+ end
35
+ @sidebar
36
+ end
37
+
38
+ def is_create_page
39
+ false
40
+ end
41
+
42
+ def is_edit_page
43
+ true
44
+ end
45
+
46
+ def format
47
+ @format = (@page.format || false) if @format.nil?
48
+ @format.to_s.downcase
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,13 @@
1
+ module Precious
2
+ module Editable
3
+ def formats(selected = @page.format)
4
+ Gollum::Page::FORMAT_NAMES.map do |key, val|
5
+ { :name => val,
6
+ :id => key.to_s,
7
+ :selected => selected == key}
8
+ end.sort do |a, b|
9
+ a[:name].downcase <=> b[:name].downcase
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,7 @@
1
+ module Precious
2
+ module Views
3
+ class Error < Layout
4
+ attr_reader :message
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,44 @@
1
+ module Precious
2
+ module Views
3
+ class History < Layout
4
+ attr_reader :page, :page_num
5
+
6
+ def title
7
+ @page.title
8
+ end
9
+
10
+ def versions
11
+ i = @versions.size + 1
12
+ @versions.map do |v|
13
+ i -= 1
14
+ { :id => v.id,
15
+ :id7 => v.id[0..6],
16
+ :num => i,
17
+ :selected => @page.version.id == v.id,
18
+ :author => v.author.name,
19
+ :message => v.message,
20
+ :date => v.committed_date.strftime("%B %d, %Y"),
21
+ :gravatar => Digest::MD5.hexdigest(v.author.email) }
22
+ end
23
+ end
24
+
25
+ def previous_link
26
+ label = "&laquo; Previous"
27
+ if @page_num == 1
28
+ %(<span class="disabled">#{label}</span>)
29
+ else
30
+ %(<a href="/history/#{@page.name}?page=#{@page_num-1}" hotkey="h">#{label}</a>)
31
+ end
32
+ end
33
+
34
+ def next_link
35
+ label = "Next &raquo;"
36
+ if @versions.size == Gollum::Page.per_page
37
+ %(<a href="/history/#{@page.name}?page=#{@page_num+1}" hotkey="l">#{label}</a>)
38
+ else
39
+ %(<span class="disabled">#{label}</span>)
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,20 @@
1
+ require 'cgi'
2
+
3
+ module Precious
4
+ module Views
5
+ class Layout < Mustache
6
+ include Rack::Utils
7
+ alias_method :h, :escape_html
8
+
9
+ attr_reader :name
10
+
11
+ def escaped_name
12
+ CGI.escape(@name)
13
+ end
14
+
15
+ def title
16
+ "Home"
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,57 @@
1
+ module Precious
2
+ module Views
3
+ class Page < Layout
4
+ attr_reader :content, :page, :footer
5
+ DATE_FORMAT = "%Y-%m-%d %H:%M:%S"
6
+ DEFAULT_AUTHOR = 'you'
7
+
8
+ def title
9
+ @page.title
10
+ end
11
+
12
+ def format
13
+ @page.format.to_s
14
+ end
15
+
16
+ def author
17
+ return DEFAULT_AUTHOR unless @page.version
18
+ @page.version.author.name
19
+ end
20
+
21
+ def date
22
+ return Time.now.strftime(DATE_FORMAT) unless @page.version
23
+ @page.version.authored_date.strftime(DATE_FORMAT)
24
+ end
25
+
26
+ def editable
27
+ @editable
28
+ end
29
+
30
+ def has_footer
31
+ @footer = (@page.footer || false) if @footer.nil?
32
+ !!@footer
33
+ end
34
+
35
+ def footer_content
36
+ has_footer && @footer.formatted_data
37
+ end
38
+
39
+ def footer_format
40
+ has_footer && @footer.format.to_s
41
+ end
42
+
43
+ def has_sidebar
44
+ @sidebar = (@page.sidebar || false) if @sidebar.nil?
45
+ !!@sidebar
46
+ end
47
+
48
+ def sidebar_content
49
+ has_sidebar && @sidebar.formatted_data
50
+ end
51
+
52
+ def sidebar_format
53
+ has_sidebar && @sidebar.format.to_s
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,19 @@
1
+ module Precious
2
+ module Views
3
+ class Pages < Layout
4
+ attr_reader :results, :ref
5
+
6
+ def title
7
+ "All pages in #{@ref}"
8
+ end
9
+
10
+ def has_results
11
+ !@results.empty?
12
+ end
13
+
14
+ def no_results
15
+ @results.empty?
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,20 @@
1
+ module Precious
2
+ module Views
3
+ class Search < Layout
4
+ attr_reader :content, :page, :footer, :results, :query
5
+
6
+ def title
7
+ "Search results for " + @query
8
+ end
9
+
10
+ def has_results
11
+ !@results.empty?
12
+ end
13
+
14
+ def no_results
15
+ @results.empty?
16
+ end
17
+
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,248 @@
1
+ module Gollum
2
+ # Controls all access to the Git objects from Gollum. Extend this class to
3
+ # add custom caching for special cases.
4
+ class GitAccess
5
+ # Initializes the GitAccess instance.
6
+ #
7
+ # path - The String path to the Git repository that holds the
8
+ # Gollum site.
9
+ # page_file_dir - String the directory in which all page files reside
10
+ #
11
+ # Returns this instance.
12
+ def initialize(path, page_file_dir = nil)
13
+ @page_file_dir = page_file_dir
14
+ @path = path
15
+ @repo = Grit::Repo.new(path)
16
+ clear
17
+ end
18
+
19
+ # Public: Determines whether the Git repository exists on disk.
20
+ #
21
+ # Returns true if it exists, or false.
22
+ def exist?
23
+ @repo.git.exist?
24
+ end
25
+
26
+ # Public: Converts a given Git reference to a SHA, using the cache if
27
+ # available.
28
+ #
29
+ # ref - a String Git reference (ex: "master")
30
+ #
31
+ # Returns a String, or nil if the ref isn't found.
32
+ def ref_to_sha(ref)
33
+ ref = ref.to_s
34
+ return if ref.empty?
35
+ sha =
36
+ if sha?(ref)
37
+ ref
38
+ else
39
+ get_cache(:ref, ref) { ref_to_sha!(ref) }
40
+ end.to_s
41
+ sha.empty? ? nil : sha
42
+ end
43
+
44
+ # Public: Gets a recursive list of Git blobs for the whole tree at the
45
+ # given commit.
46
+ #
47
+ # ref - A String Git reference or Git SHA to a commit.
48
+ #
49
+ # Returns an Array of BlobEntry instances.
50
+ def tree(ref)
51
+ if sha = ref_to_sha(ref)
52
+ get_cache(:tree, sha) { tree!(sha) }
53
+ else
54
+ []
55
+ end
56
+ end
57
+
58
+ # Public: Fetches the contents of the Git blob at the given SHA.
59
+ #
60
+ # sha - A String Git SHA.
61
+ #
62
+ # Returns the String content of the blob.
63
+ def blob(sha)
64
+ cat_file!(sha)
65
+ end
66
+
67
+ # Public: Looks up the Git commit using the given Git SHA or ref.
68
+ #
69
+ # ref - A String Git SHA or ref.
70
+ #
71
+ # Returns a Grit::Commit.
72
+ def commit(ref)
73
+ if sha?(ref)
74
+ get_cache(:commit, ref) { commit!(ref) }
75
+ else
76
+ if sha = get_cache(:ref, ref)
77
+ commit(sha)
78
+ else
79
+ if cm = commit!(ref)
80
+ set_cache(:ref, ref, cm.id)
81
+ set_cache(:commit, cm.id, cm)
82
+ end
83
+ end
84
+ end
85
+ end
86
+
87
+ # Public: Clears all of the cached data that this GitAccess is tracking.
88
+ #
89
+ # Returns nothing.
90
+ def clear
91
+ @ref_map = {}
92
+ @tree_map = {}
93
+ @commit_map = {}
94
+ end
95
+
96
+ # Public: Refreshes just the cached Git reference data. This should
97
+ # be called after every Gollum update.
98
+ #
99
+ # Returns nothing.
100
+ def refresh
101
+ @ref_map.clear
102
+ end
103
+
104
+ #########################################################################
105
+ #
106
+ # Internal Methods
107
+ #
108
+ #########################################################################
109
+
110
+ # Gets the String path to the Git repository.
111
+ attr_reader :path
112
+
113
+ # Gets the Grit::Repo instance for the Git repository.
114
+ attr_reader :repo
115
+
116
+ # Gets a Hash cache of refs to commit SHAs.
117
+ #
118
+ # {"master" => "abc123", ...}
119
+ #
120
+ attr_reader :ref_map
121
+
122
+ # Gets a Hash cache of commit SHAs to a recursive tree of blobs.
123
+ #
124
+ # {"abc123" => [<BlobEntry>, <BlobEntry>]}
125
+ #
126
+ attr_reader :tree_map
127
+
128
+ # Gets a Hash cache of commit SHAs to the Grit::Commit instance.
129
+ #
130
+ # {"abcd123" => <Grit::Commit>}
131
+ #
132
+ attr_reader :commit_map
133
+
134
+ # Checks to see if the given String is a 40 character hex SHA.
135
+ #
136
+ # str - Possible String SHA.
137
+ #
138
+ # Returns true if the String is a SHA, or false.
139
+ def sha?(str)
140
+ !!(str =~ /^[0-9a-f]{40}$/)
141
+ end
142
+
143
+ # Looks up the Git SHA for the given Git ref.
144
+ #
145
+ # ref - String Git ref.
146
+ #
147
+ # Returns a String SHA.
148
+ def ref_to_sha!(ref)
149
+ @repo.git.rev_list({:max_count=>1}, ref)
150
+ rescue Grit::GitRuby::Repository::NoSuchShaFound
151
+ end
152
+
153
+ # Looks up the Git blobs for a given commit.
154
+ #
155
+ # sha - String commit SHA.
156
+ #
157
+ # Returns an Array of BlobEntry instances.
158
+ def tree!(sha)
159
+ tree = @repo.git.native(:ls_tree,
160
+ {:r => true, :l => true, :z => true}, sha)
161
+ if tree.respond_to?(:force_encoding)
162
+ tree.force_encoding("UTF-8")
163
+ end
164
+ items = tree.split("\0").inject([]) do |memo, line|
165
+ memo << parse_tree_line(line)
166
+ end
167
+
168
+ if dir = @page_file_dir
169
+ regex = /^#{dir}\//
170
+ items.select { |i| i.path =~ regex }
171
+ else
172
+ items
173
+ end
174
+ end
175
+
176
+ # Reads the content from the Git db at the given SHA.
177
+ #
178
+ # sha - The String SHA.
179
+ #
180
+ # Returns the String content of the Git object.
181
+ def cat_file!(sha)
182
+ @repo.git.cat_file({:p => true}, sha)
183
+ end
184
+
185
+ # Reads a Git commit.
186
+ #
187
+ # sha - The string SHA of the Git commit.
188
+ #
189
+ # Returns a Grit::Commit.
190
+ def commit!(sha)
191
+ @repo.commit(sha)
192
+ end
193
+
194
+ # Attempts to get the given data from a cache. If it doesn't exist, it'll
195
+ # pass the results of the yielded block to the cache for future accesses.
196
+ #
197
+ # name - The cache prefix used in building the full cache key.
198
+ # key - The unique cache key suffix, usually a String Git SHA.
199
+ #
200
+ # Yields a block to pass to the cache.
201
+ # Returns the cached result.
202
+ def get_cache(name, key)
203
+ cache = instance_variable_get("@#{name}_map")
204
+ value = cache[key]
205
+ if value.nil? && block_given?
206
+ set_cache(name, key, value = yield)
207
+ end
208
+ value == :_nil ? nil : value
209
+ end
210
+
211
+ # Writes some data to the internal cache.
212
+ #
213
+ # name - The cache prefix used in building the full cache key.
214
+ # key - The unique cache key suffix, usually a String Git SHA.
215
+ # value - The value to write to the cache.
216
+ #
217
+ # Returns nothing.
218
+ def set_cache(name, key, value)
219
+ cache = instance_variable_get("@#{name}_map")
220
+ cache[key] = value || :_nil
221
+ end
222
+
223
+ # Parses a line of output from the `ls-tree` command.
224
+ #
225
+ # line - A String line of output:
226
+ # "100644 blob 839c2291b30495b9a882c17d08254d3c90d8fb53 Home.md"
227
+ #
228
+ # Returns an Array of BlobEntry instances.
229
+ def parse_tree_line(line)
230
+ mode, type, sha, size, *name = line.split(/\s+/)
231
+ BlobEntry.new(sha, name.join(' '), size.to_i)
232
+ end
233
+
234
+ # Decode octal sequences (\NNN) in tree path names.
235
+ #
236
+ # path - String path name.
237
+ #
238
+ # Returns a decoded String.
239
+ def decode_git_path(path)
240
+ if path[0] == ?" && path[-1] == ?"
241
+ path = path[1...-1]
242
+ path.gsub!(/\\\d{3}/) { |m| m[1..-1].to_i(8).chr }
243
+ end
244
+ path.gsub!(/\\[rn"\\]/) { |m| eval(%("#{m.to_s}")) }
245
+ path
246
+ end
247
+ end
248
+ end