smeagol 0.5.9 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
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