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.
Files changed (59) hide show
  1. data/.ruby +80 -0
  2. data/.yardopts +9 -0
  3. data/HISTORY.md +147 -0
  4. data/LICENSE.txt +30 -0
  5. data/README.md +132 -26
  6. data/bin/smeagol +9 -39
  7. data/bin/smeagol-init +3 -0
  8. data/bin/smeagol-preview +4 -0
  9. data/bin/smeagol-serve +4 -0
  10. data/bin/smeagol-update +4 -0
  11. data/bin/smeagold +1 -4
  12. data/lib/smeagol.rb +77 -6
  13. data/lib/smeagol/app.rb +121 -75
  14. data/lib/smeagol/cache.rb +43 -24
  15. data/lib/smeagol/cli.rb +166 -0
  16. data/lib/smeagol/config.rb +154 -0
  17. data/lib/smeagol/console.rb +369 -0
  18. data/lib/smeagol/controller.rb +275 -0
  19. data/lib/smeagol/core_ext.rb +12 -0
  20. data/lib/smeagol/gollum/blob_entry.rb +17 -0
  21. data/lib/smeagol/gollum/file.rb +44 -0
  22. data/lib/smeagol/gollum/page.rb +47 -0
  23. data/lib/smeagol/gollum/wiki.rb +31 -0
  24. data/lib/smeagol/helpers/rss.rb +96 -0
  25. data/lib/smeagol/helpers/toc.rb +69 -0
  26. data/lib/smeagol/public/assets/smeagol/gollum.css +716 -0
  27. data/lib/smeagol/public/{smeagol → assets/smeagol}/html5.js +0 -0
  28. data/lib/smeagol/public/{smeagol → assets/smeagol}/pygment.css +0 -0
  29. data/lib/smeagol/public/assets/smeagol/template.css +631 -0
  30. data/lib/smeagol/repository.rb +85 -0
  31. data/lib/smeagol/settings.rb +302 -0
  32. data/lib/smeagol/templates/layouts/page.mustache +19 -0
  33. data/lib/smeagol/templates/layouts/versions.mustache +16 -0
  34. data/lib/smeagol/templates/partials/footer.mustache +17 -0
  35. data/lib/smeagol/templates/partials/header.mustache +47 -0
  36. data/lib/smeagol/templates/settings.yml +64 -0
  37. data/lib/smeagol/version.rb +1 -1
  38. data/lib/smeagol/views/base.rb +188 -27
  39. data/lib/smeagol/views/form.rb +56 -0
  40. data/lib/smeagol/views/page.rb +126 -25
  41. data/lib/smeagol/views/post.rb +51 -0
  42. data/lib/smeagol/views/versions.rb +20 -6
  43. data/lib/smeagol/wiki.rb +25 -45
  44. data/test/helper.rb +72 -8
  45. data/test/test_app.rb +57 -0
  46. data/test/test_cache.rb +10 -11
  47. data/test/test_init.rb +27 -0
  48. data/test/test_update.rb +20 -0
  49. data/test/test_wiki.rb +13 -10
  50. metadata +142 -216
  51. data/bin/smeagol-static +0 -115
  52. data/lib/file.rb +0 -10
  53. data/lib/smeagol/hash.rb +0 -13
  54. data/lib/smeagol/option_parser.rb +0 -138
  55. data/lib/smeagol/public/smeagol/main.css +0 -234
  56. data/lib/smeagol/templates/page.mustache +0 -58
  57. data/lib/smeagol/templates/versions.mustache +0 -50
  58. data/test/test_file.rb +0 -12
  59. 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,12 @@
1
+ require 'ostruct'
2
+
3
+ class OpenStruct
4
+ def [](key)
5
+ __send__(key)
6
+ end
7
+
8
+ def []=(key, value)
9
+ __send__("#{key}=", value)
10
+ end
11
+ end
12
+
@@ -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