read-only-gollum 1.4.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.
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