smeagol 0.5.9 → 0.6.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/.ruby +80 -0
- data/.yardopts +9 -0
- data/HISTORY.md +147 -0
- data/LICENSE.txt +30 -0
- data/README.md +132 -26
- data/bin/smeagol +9 -39
- data/bin/smeagol-init +3 -0
- data/bin/smeagol-preview +4 -0
- data/bin/smeagol-serve +4 -0
- data/bin/smeagol-update +4 -0
- data/bin/smeagold +1 -4
- data/lib/smeagol.rb +77 -6
- data/lib/smeagol/app.rb +121 -75
- data/lib/smeagol/cache.rb +43 -24
- data/lib/smeagol/cli.rb +166 -0
- data/lib/smeagol/config.rb +154 -0
- data/lib/smeagol/console.rb +369 -0
- data/lib/smeagol/controller.rb +275 -0
- data/lib/smeagol/core_ext.rb +12 -0
- data/lib/smeagol/gollum/blob_entry.rb +17 -0
- data/lib/smeagol/gollum/file.rb +44 -0
- data/lib/smeagol/gollum/page.rb +47 -0
- data/lib/smeagol/gollum/wiki.rb +31 -0
- data/lib/smeagol/helpers/rss.rb +96 -0
- data/lib/smeagol/helpers/toc.rb +69 -0
- data/lib/smeagol/public/assets/smeagol/gollum.css +716 -0
- data/lib/smeagol/public/{smeagol → assets/smeagol}/html5.js +0 -0
- data/lib/smeagol/public/{smeagol → assets/smeagol}/pygment.css +0 -0
- data/lib/smeagol/public/assets/smeagol/template.css +631 -0
- data/lib/smeagol/repository.rb +85 -0
- data/lib/smeagol/settings.rb +302 -0
- data/lib/smeagol/templates/layouts/page.mustache +19 -0
- data/lib/smeagol/templates/layouts/versions.mustache +16 -0
- data/lib/smeagol/templates/partials/footer.mustache +17 -0
- data/lib/smeagol/templates/partials/header.mustache +47 -0
- data/lib/smeagol/templates/settings.yml +64 -0
- data/lib/smeagol/version.rb +1 -1
- data/lib/smeagol/views/base.rb +188 -27
- data/lib/smeagol/views/form.rb +56 -0
- data/lib/smeagol/views/page.rb +126 -25
- data/lib/smeagol/views/post.rb +51 -0
- data/lib/smeagol/views/versions.rb +20 -6
- data/lib/smeagol/wiki.rb +25 -45
- data/test/helper.rb +72 -8
- data/test/test_app.rb +57 -0
- data/test/test_cache.rb +10 -11
- data/test/test_init.rb +27 -0
- data/test/test_update.rb +20 -0
- data/test/test_wiki.rb +13 -10
- metadata +142 -216
- data/bin/smeagol-static +0 -115
- data/lib/file.rb +0 -10
- data/lib/smeagol/hash.rb +0 -13
- data/lib/smeagol/option_parser.rb +0 -138
- data/lib/smeagol/public/smeagol/main.css +0 -234
- data/lib/smeagol/templates/page.mustache +0 -58
- data/lib/smeagol/templates/versions.mustache +0 -50
- data/test/test_file.rb +0 -12
- data/test/test_hash.rb +0 -18
@@ -0,0 +1,275 @@
|
|
1
|
+
module Smeagol
|
2
|
+
|
3
|
+
# Shared controller.
|
4
|
+
class Controller
|
5
|
+
|
6
|
+
# Initialize new Controller instance.
|
7
|
+
#
|
8
|
+
# wiki - Gollum::Wiki
|
9
|
+
#
|
10
|
+
def initialize(wiki)
|
11
|
+
@wiki = wiki
|
12
|
+
#@views = Hash.new{ |h,k| h[k]={} }
|
13
|
+
#@media = Hash.new{ |h,k| h[k]={} }
|
14
|
+
#@preloaded = {}
|
15
|
+
end
|
16
|
+
|
17
|
+
# Access to Gollum::Wiki.
|
18
|
+
attr :wiki
|
19
|
+
|
20
|
+
# Public: The Smeagol wiki settings. These can be found in the _settings.yml
|
21
|
+
# file at the root of the repository.
|
22
|
+
# This method caches the settings for all subsequent calls.
|
23
|
+
#
|
24
|
+
# TODO: Should settings be coming from current file or from repo version?
|
25
|
+
# This can be tricky. Right now they come from current file, but
|
26
|
+
# In future we probably may need to split this into two config files.
|
27
|
+
# One that comes from version and one that is current.
|
28
|
+
#
|
29
|
+
# Returns a Settings object.
|
30
|
+
def settings
|
31
|
+
@settings ||= Settings.load(wiki.path)
|
32
|
+
end
|
33
|
+
|
34
|
+
#def view(wiki_file, version='master')
|
35
|
+
# @views[version][wiki_file] ||= create_view(wiki_file, version)
|
36
|
+
#end
|
37
|
+
|
38
|
+
# Lookup view by wiki file and version.
|
39
|
+
def view(wiki_file, version='master')
|
40
|
+
case wiki_file
|
41
|
+
when Gollum::Page
|
42
|
+
if wiki_file.post?
|
43
|
+
Views::Post.new(self, wiki_file, version)
|
44
|
+
else
|
45
|
+
Views::Page.new(self, wiki_file, version)
|
46
|
+
end
|
47
|
+
when Gollum::File
|
48
|
+
if wiki_file.extname == '.mustache'
|
49
|
+
Views::Form.new(self, wiki_file, version)
|
50
|
+
else
|
51
|
+
nil #Views::Raw.new(self, wiki_file, version)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# Returns a list of filtered wiki files.
|
57
|
+
def wiki_files
|
58
|
+
filter(wiki.files + wiki.pages)
|
59
|
+
end
|
60
|
+
|
61
|
+
# Collect a list of all views.
|
62
|
+
def views(version='master')
|
63
|
+
list = []
|
64
|
+
wiki_files.each do |file|
|
65
|
+
view = view(file, version)
|
66
|
+
list << view if view
|
67
|
+
end
|
68
|
+
list
|
69
|
+
end
|
70
|
+
|
71
|
+
# Collect a list of all posts.
|
72
|
+
def posts(version='master')
|
73
|
+
list = []
|
74
|
+
wiki_files.each do |file|
|
75
|
+
next unless Gollum::Page === file && file.post?
|
76
|
+
list << view(file, version)
|
77
|
+
end
|
78
|
+
list
|
79
|
+
end
|
80
|
+
|
81
|
+
# Returns Array of views.
|
82
|
+
#def views(version='master')
|
83
|
+
# wiki_files.map{ |wf| view(wf) }.compact
|
84
|
+
#end
|
85
|
+
|
86
|
+
# Media are raw files that simply need to be passed long.
|
87
|
+
# Unlike assets they are versioned, but they do not need
|
88
|
+
# to be rendered via a view.
|
89
|
+
#def media(version='master')
|
90
|
+
# preload(version)
|
91
|
+
# @media[version].values
|
92
|
+
#end
|
93
|
+
|
94
|
+
# Collect a list of all files in assets directory.
|
95
|
+
# These files are never versioned.
|
96
|
+
def assets
|
97
|
+
files = collect_files(wiki.path, 'assets')
|
98
|
+
filter(files)
|
99
|
+
end
|
100
|
+
|
101
|
+
=begin
|
102
|
+
#
|
103
|
+
def preload(version='master')
|
104
|
+
@preloaded[version] ||= (
|
105
|
+
filtered_files.each do |file|
|
106
|
+
if file.extname == '.mustache'
|
107
|
+
view(file, version)
|
108
|
+
else
|
109
|
+
@media[version][file] = file
|
110
|
+
end
|
111
|
+
end
|
112
|
+
filtered_pages.each do |page|
|
113
|
+
view(page, version)
|
114
|
+
end
|
115
|
+
true
|
116
|
+
)
|
117
|
+
end
|
118
|
+
=end
|
119
|
+
|
120
|
+
|
121
|
+
#
|
122
|
+
#def filtered_files
|
123
|
+
# @filtered_files ||= filter(wiki.files)
|
124
|
+
#end
|
125
|
+
|
126
|
+
#
|
127
|
+
#def filtered_pages
|
128
|
+
# @filtered_pages ||= filter(wiki.pages)
|
129
|
+
#end
|
130
|
+
|
131
|
+
#
|
132
|
+
def collect_files(base, offset)
|
133
|
+
list = []
|
134
|
+
dir = ::File.join(base, offset)
|
135
|
+
return list unless File.directory?(dir)
|
136
|
+
|
137
|
+
::Dir.entries(dir).each do |path|
|
138
|
+
next if path == '.' or path == '..'
|
139
|
+
subdir = ::File.join(dir, path)
|
140
|
+
if ::File.directory?(subdir)
|
141
|
+
sublist = collect_files(base, File.join(offset, path))
|
142
|
+
list.concat(sublist)
|
143
|
+
else
|
144
|
+
list << ::File.join(offset, path)
|
145
|
+
end
|
146
|
+
end
|
147
|
+
list
|
148
|
+
end
|
149
|
+
|
150
|
+
# Filter files according to settings `include` and `exclude` fields.
|
151
|
+
# Selection block can be given to further filter the list.
|
152
|
+
#
|
153
|
+
# files - Array of wiki files to be filtered.
|
154
|
+
#
|
155
|
+
# Returns [Array<String>].
|
156
|
+
def filter(files, &selection)
|
157
|
+
result = []
|
158
|
+
files.map do |file|
|
159
|
+
path = (String === file ? file : file.path)
|
160
|
+
unless settings.include.any?{ |x| File.fnmatch?(x, path) }
|
161
|
+
# TODO: If we enforce the use of underscore the we would
|
162
|
+
# not need to filter out settings, partials and static locations.
|
163
|
+
# exclude settings file
|
164
|
+
next if path == Settings::FILE
|
165
|
+
# # exlcude assets
|
166
|
+
# next if path.index('assets') == 0
|
167
|
+
# exclude template directory (TODO: future version may allow this)
|
168
|
+
next if path.index(settings.partials) == 0
|
169
|
+
# exclude any files starting with `.` or `_`
|
170
|
+
next if path.split('/').any? do |x|
|
171
|
+
x.start_with?('_') or x.start_with?('.')
|
172
|
+
end
|
173
|
+
# exclude any files specifically exluded by settings
|
174
|
+
next if settings.exclude.any? do |x|
|
175
|
+
::File.fnmatch?(x, path) ||
|
176
|
+
x.end_with?('/') && path.index(x) == 0
|
177
|
+
end
|
178
|
+
end
|
179
|
+
result << file
|
180
|
+
end
|
181
|
+
result = result.select(&selection) if selection
|
182
|
+
result
|
183
|
+
end
|
184
|
+
|
185
|
+
# Render wiki file.
|
186
|
+
#
|
187
|
+
# wiki_file - Gollum::Page or Gollum::File.
|
188
|
+
# version - Commit id, branch or tag.
|
189
|
+
#
|
190
|
+
# Returns [String].
|
191
|
+
def render(wiki_file, version='master')
|
192
|
+
view = view(wiki_file, version)
|
193
|
+
render_view(view)
|
194
|
+
end
|
195
|
+
|
196
|
+
# Render view.
|
197
|
+
#
|
198
|
+
# view - Views::Base subclass.
|
199
|
+
#
|
200
|
+
# Returns [String].
|
201
|
+
def render_view(view)
|
202
|
+
if view.layout
|
203
|
+
content = Mustache.render(view.layout, view)
|
204
|
+
else
|
205
|
+
content = view.content
|
206
|
+
end
|
207
|
+
|
208
|
+
return content
|
209
|
+
end
|
210
|
+
|
211
|
+
## For static sites we cannot depend on the web server to default a link
|
212
|
+
## to a directory to the index.html file within it. So we need to append
|
213
|
+
## index.html to any href links for which we have wiki pages.
|
214
|
+
## This is not a prefect solution, but there may not be a better one.
|
215
|
+
##
|
216
|
+
#def index_directory_hrefs(html)
|
217
|
+
# html.gsub(/href=\"(.*)\"/) do |match|
|
218
|
+
# link = "#{$1}/index.html"
|
219
|
+
# if @pages[link] #if File.directory?(File.join(current_directory, $1))
|
220
|
+
# "href=\"#{link}\""
|
221
|
+
# else
|
222
|
+
# match # no change
|
223
|
+
# end
|
224
|
+
# end
|
225
|
+
# end
|
226
|
+
|
227
|
+
# Public: Get a list of plugin files.
|
228
|
+
#
|
229
|
+
# Returns Array of plugin files.
|
230
|
+
#def plugins
|
231
|
+
# files.map do |f|
|
232
|
+
# File.fnmatch?('_plugins/*.rb', f.path)
|
233
|
+
# end
|
234
|
+
#end
|
235
|
+
|
236
|
+
=begin
|
237
|
+
# Collect list of pages.
|
238
|
+
def pages
|
239
|
+
@pages ||= filtered_pages.select{ |p| !p.post? }
|
240
|
+
end
|
241
|
+
|
242
|
+
# Collect list of posts.
|
243
|
+
def posts
|
244
|
+
@posts ||= filtered_pages.select{ |p| p.post? }
|
245
|
+
end
|
246
|
+
|
247
|
+
# Collect list of non-page files to be rendered.
|
248
|
+
def files
|
249
|
+
@files ||= filtered_files.select{ |f| f.extname == '.mustache' }
|
250
|
+
end
|
251
|
+
|
252
|
+
# Collect list of raw asset files.
|
253
|
+
def assets
|
254
|
+
@assets ||= filtered_files.select{ |f| f.extname != '.mustache' }
|
255
|
+
end
|
256
|
+
|
257
|
+
#
|
258
|
+
def page_views(version='master')
|
259
|
+
@page_views ||= pages.map{ |page| Views::Page.new(page, version) }
|
260
|
+
end
|
261
|
+
|
262
|
+
#
|
263
|
+
def post_views(version='master')
|
264
|
+
@post_views ||= posts.map{ |post| Views::Post.new(post, version) }
|
265
|
+
end
|
266
|
+
|
267
|
+
#
|
268
|
+
def file_views(version='master')
|
269
|
+
@file_views ||= files.map{ |post| Views::Template.new(file, versionb) }
|
270
|
+
end
|
271
|
+
=end
|
272
|
+
|
273
|
+
end
|
274
|
+
|
275
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Gollum
|
2
|
+
class BlobEntry
|
3
|
+
|
4
|
+
# Gets a File instance for this blob.
|
5
|
+
#
|
6
|
+
# wiki - Gollum::Wiki instance for the Gollum::Page
|
7
|
+
#
|
8
|
+
# Returns a Gollum::File instance.
|
9
|
+
def file(wiki, commit)
|
10
|
+
blob = self.blob(wiki.repo)
|
11
|
+
file = wiki.file_class.new(wiki).populate(blob, self.dir)
|
12
|
+
file.version = commit
|
13
|
+
file
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Gollum
|
2
|
+
class File
|
3
|
+
|
4
|
+
#alias :filename :name
|
5
|
+
|
6
|
+
# Populate the Page with information from the Blob.
|
7
|
+
#
|
8
|
+
# blob - The Grit::Blob that contains the info.
|
9
|
+
# path - The String directory path of the page file.
|
10
|
+
#
|
11
|
+
# Returns the populated Gollum::Page.
|
12
|
+
def populate(blob, path=nil)
|
13
|
+
@blob = blob
|
14
|
+
@path = "#{path}/#{blob.name}"[1..-1]
|
15
|
+
self
|
16
|
+
end
|
17
|
+
|
18
|
+
#
|
19
|
+
attr_reader :wiki
|
20
|
+
|
21
|
+
# Public: The current version of the page.
|
22
|
+
#
|
23
|
+
# Returns the Grit::Commit.
|
24
|
+
attr_accessor :version
|
25
|
+
|
26
|
+
# Recent addition to Gollum.
|
27
|
+
alias filename name unless method_defined?(:filename)
|
28
|
+
|
29
|
+
# Public: The title will be constructed from the
|
30
|
+
# filename by stripping the extension and replacing any dashes with
|
31
|
+
# spaces.
|
32
|
+
#
|
33
|
+
# Returns the fully sanitized String title.
|
34
|
+
def title
|
35
|
+
Sanitize.clean(name).strip
|
36
|
+
end
|
37
|
+
|
38
|
+
#
|
39
|
+
def extname
|
40
|
+
::File.extname(path)
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module Gollum
|
2
|
+
|
3
|
+
class Page
|
4
|
+
def post?
|
5
|
+
/^(\d\d\d\d-\d\d-\d\d)/.match(filename)
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
class Markup
|
10
|
+
|
11
|
+
# Attempt to process the tag as a page link tag.
|
12
|
+
#
|
13
|
+
# tag - The String tag contents (the stuff inside the double
|
14
|
+
# brackets).
|
15
|
+
#
|
16
|
+
# Returns the String HTML if the tag is a valid page link tag or nil
|
17
|
+
# if it is not.
|
18
|
+
def process_page_link_tag(tag)
|
19
|
+
parts = tag.split('|')
|
20
|
+
parts.reverse! if @format == :mediawiki
|
21
|
+
|
22
|
+
name, page_name = *parts.compact.map(&:strip)
|
23
|
+
cname = @wiki.page_class.cname(page_name || name)
|
24
|
+
|
25
|
+
if name =~ %r{^https?://} && page_name.nil?
|
26
|
+
%{<a href="#{name}">#{name}</a>}
|
27
|
+
else
|
28
|
+
presence = "absent"
|
29
|
+
link_name = cname
|
30
|
+
page, extra = find_page_from_name(cname)
|
31
|
+
if page
|
32
|
+
link_name = @wiki.page_class.cname(page.name)
|
33
|
+
presence = "present"
|
34
|
+
end
|
35
|
+
link = ::File.join(@wiki.base_path, CGI.escape(link_name))
|
36
|
+
|
37
|
+
# TODO: This is a temporary hack for posts until actual subdirs are supported.
|
38
|
+
# Also, this needs to be improved so /\d-/ does match if part of actual title.
|
39
|
+
link = link.gsub(/(\d)-/, '\1/')
|
40
|
+
|
41
|
+
%{<a class="internal #{presence}" href="#{link}#{extra}">#{name}</a>}
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Gollum
|
2
|
+
class Wiki
|
3
|
+
|
4
|
+
# Public: Lists all non-page files for this wiki.
|
5
|
+
#
|
6
|
+
# treeish - The String commit ID or ref to find (default: @ref)
|
7
|
+
#
|
8
|
+
# Returns an Array of Gollum::File instances.
|
9
|
+
def files(treeish = nil)
|
10
|
+
file_list(treeish || @ref)
|
11
|
+
end
|
12
|
+
|
13
|
+
# Fill an array with a list of files.
|
14
|
+
#
|
15
|
+
# ref - A String ref that is either a commit SHA or references one.
|
16
|
+
#
|
17
|
+
# Returns a flat Array of Gollum::File instances.
|
18
|
+
def file_list(ref)
|
19
|
+
if sha = @access.ref_to_sha(ref)
|
20
|
+
commit = @access.commit(sha)
|
21
|
+
tree_map_for(sha).inject([]) do |list, entry|
|
22
|
+
next list if @page_class.valid_page_name?(entry.name)
|
23
|
+
list << entry.file(self, commit)
|
24
|
+
end
|
25
|
+
else
|
26
|
+
[]
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
# TODO: Techincally @pages should probably be called @files since
|
2
|
+
# it includes all files that end up in the site.
|
3
|
+
|
4
|
+
require 'rss/maker'
|
5
|
+
|
6
|
+
module Smeagol
|
7
|
+
class RSS
|
8
|
+
#
|
9
|
+
# Initialize a new Smeagol::RSS instance.
|
10
|
+
#
|
11
|
+
def initialize(ctrl, options={})
|
12
|
+
@ctrl = ctrl
|
13
|
+
@wiki = ctrl.wiki
|
14
|
+
@version = options[:version] || 'master'
|
15
|
+
@posts = options[:posts]
|
16
|
+
end
|
17
|
+
|
18
|
+
##
|
19
|
+
## Collect the items to include in the RSS from the pages.
|
20
|
+
## Only pages with post_dates are included.
|
21
|
+
##
|
22
|
+
#def items
|
23
|
+
# list = []
|
24
|
+
# @pages.each do |page|
|
25
|
+
# next unless Smeagol::Views::Page === page
|
26
|
+
# list << page if page.post_date
|
27
|
+
# end
|
28
|
+
# list
|
29
|
+
#end
|
30
|
+
|
31
|
+
#
|
32
|
+
# Build the RSS instance and cache the result.
|
33
|
+
#
|
34
|
+
# Returns an RSS::Rss object.
|
35
|
+
#
|
36
|
+
def rss
|
37
|
+
@rss ||= build_rss
|
38
|
+
end
|
39
|
+
|
40
|
+
#
|
41
|
+
# Build an RSS instance using Ruby's RSS::Maker library.
|
42
|
+
#
|
43
|
+
# Returns an RSS::Rss object.
|
44
|
+
#
|
45
|
+
def build_rss
|
46
|
+
::RSS::Maker.make('2.0') do |maker|
|
47
|
+
maker.channel.link = @wiki.settings.url
|
48
|
+
maker.channel.title = @wiki.settings.title
|
49
|
+
maker.channel.description = @wiki.settings.description.to_s
|
50
|
+
maker.channel.author = @wiki.settings.author.to_s
|
51
|
+
maker.channel.updated = Time.now.to_s
|
52
|
+
maker.items.do_sort = true
|
53
|
+
|
54
|
+
posts.each do |post|
|
55
|
+
html = post.content
|
56
|
+
date = Time.parse(post.post_date)
|
57
|
+
|
58
|
+
next if date > Time.now unless @wiki.settings.future
|
59
|
+
|
60
|
+
if i = html.index('</p>')
|
61
|
+
text = html[0..i+4]
|
62
|
+
else
|
63
|
+
text = html
|
64
|
+
end
|
65
|
+
|
66
|
+
maker.items.new_item do |item|
|
67
|
+
item.title = post.title
|
68
|
+
item.link = File.join(@wiki.settings.url, post.href)
|
69
|
+
item.date = date
|
70
|
+
item.description = text
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
#
|
77
|
+
# Convert the RSS object to XML string.
|
78
|
+
#
|
79
|
+
def to_s
|
80
|
+
rss.to_s
|
81
|
+
end
|
82
|
+
|
83
|
+
#
|
84
|
+
def posts
|
85
|
+
@ctrl.views(@version).select{ |p| p.post? }
|
86
|
+
#@posts ||= (
|
87
|
+
# @wiki.pages.map do |page|
|
88
|
+
# next unless page.post?
|
89
|
+
# Smeagol::Views::Post.new(@ctrl, page)
|
90
|
+
# end.compact
|
91
|
+
#)
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|