olelo 0.9.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 (282) hide show
  1. data/.gitignore +10 -0
  2. data/.gitmodules +3 -0
  3. data/Gemfile +3 -0
  4. data/README.markdown +104 -0
  5. data/Rakefile +92 -0
  6. data/bin/olelo +5 -0
  7. data/config.ru +86 -0
  8. data/config/aspects.rb +256 -0
  9. data/config/config.yml.default +154 -0
  10. data/config/initializers/00-mime_types.rb +29 -0
  11. data/config/initializers/01-slim.rb +2 -0
  12. data/config/interwiki.yml +11 -0
  13. data/doc/AUTHORS +7 -0
  14. data/doc/LICENSE +22 -0
  15. data/lib/olelo.rb +39 -0
  16. data/lib/olelo/application.rb +294 -0
  17. data/lib/olelo/attributes.rb +285 -0
  18. data/lib/olelo/config.rb +88 -0
  19. data/lib/olelo/extensions.rb +252 -0
  20. data/lib/olelo/helper.rb +290 -0
  21. data/lib/olelo/hooks.rb +142 -0
  22. data/lib/olelo/html_safe.rb +29 -0
  23. data/lib/olelo/initializer.rb +76 -0
  24. data/lib/olelo/locale.rb +63 -0
  25. data/lib/olelo/locale.yml +284 -0
  26. data/lib/olelo/menu.rb +101 -0
  27. data/lib/olelo/middleware/blacklist.rb +25 -0
  28. data/lib/olelo/middleware/degrade_mime_type.rb +19 -0
  29. data/lib/olelo/middleware/flash.rb +97 -0
  30. data/lib/olelo/middleware/force_encoding.rb +41 -0
  31. data/lib/olelo/page.rb +266 -0
  32. data/lib/olelo/patch.rb +311 -0
  33. data/lib/olelo/plugin.rb +188 -0
  34. data/lib/olelo/repository.rb +225 -0
  35. data/lib/olelo/routing.rb +223 -0
  36. data/lib/olelo/templates.rb +30 -0
  37. data/lib/olelo/user.rb +132 -0
  38. data/lib/olelo/util.rb +233 -0
  39. data/lib/olelo/version.rb +3 -0
  40. data/lib/olelo/virtualfs.rb +161 -0
  41. data/lib/rack/olelo_patches.rb +33 -0
  42. data/lib/rack/relative_redirect.rb +44 -0
  43. data/lib/rack/static_cache.rb +93 -0
  44. data/lib/yard/addons.rb +1 -0
  45. data/lib/yard/addons/hook_handler.rb +25 -0
  46. data/lib/yard/addons/override_tag.rb +14 -0
  47. data/lib/yard/addons/route_handler.rb +33 -0
  48. data/lib/yard/addons/sanitize_anchor.rb +16 -0
  49. data/olelo.gemspec +31 -0
  50. data/plugins/aspects/changelog.rb +45 -0
  51. data/plugins/aspects/documentbrowser.rb +57 -0
  52. data/plugins/aspects/download.rb +11 -0
  53. data/plugins/aspects/highlight.rb +8 -0
  54. data/plugins/aspects/image.rb +41 -0
  55. data/plugins/aspects/imageinfo.rb +64 -0
  56. data/plugins/aspects/locale.yml +60 -0
  57. data/plugins/aspects/main.rb +199 -0
  58. data/plugins/aspects/pageinfo.rb +37 -0
  59. data/plugins/aspects/source.rb +6 -0
  60. data/plugins/aspects/subpages.rb +44 -0
  61. data/plugins/aspects/text.rb +9 -0
  62. data/plugins/blog/blog.css +1 -0
  63. data/plugins/blog/blog.scss +37 -0
  64. data/plugins/blog/locale.yml +12 -0
  65. data/plugins/blog/main.rb +85 -0
  66. data/plugins/editor/locale.yml +18 -0
  67. data/plugins/editor/markup/main.rb +3 -0
  68. data/plugins/editor/markup/script.js +10 -0
  69. data/plugins/editor/markup/script/00-jquery.textselection.js +267 -0
  70. data/plugins/editor/markup/script/01-olelo.markupeditor.js +116 -0
  71. data/plugins/editor/markup/script/init.js +10 -0
  72. data/plugins/editor/preview.rb +52 -0
  73. data/plugins/editor/recaptcha.rb +56 -0
  74. data/plugins/filters/creole.rb +37 -0
  75. data/plugins/filters/disposition.rb +9 -0
  76. data/plugins/filters/editsection.rb +67 -0
  77. data/plugins/filters/fix_img_tag.rb +16 -0
  78. data/plugins/filters/html_wrapper.rb +12 -0
  79. data/plugins/filters/interwiki.rb +19 -0
  80. data/plugins/filters/link_classifier.rb +26 -0
  81. data/plugins/filters/locale.yml +15 -0
  82. data/plugins/filters/main.rb +202 -0
  83. data/plugins/filters/markdown_nowiki.rb +15 -0
  84. data/plugins/filters/numbering.xsl +93 -0
  85. data/plugins/filters/orgmode.rb +6 -0
  86. data/plugins/filters/rubypants.rb +6 -0
  87. data/plugins/filters/s5/main.rb +32 -0
  88. data/plugins/filters/s5/s5.xsl +118 -0
  89. data/plugins/filters/tilt.rb +17 -0
  90. data/plugins/filters/toc.rb +50 -0
  91. data/plugins/filters/xhtml2latex.xsl +232 -0
  92. data/plugins/filters/xslt.rb +22 -0
  93. data/plugins/gallery/gallery.css +1 -0
  94. data/plugins/gallery/gallery.scss +28 -0
  95. data/plugins/gallery/main.rb +34 -0
  96. data/plugins/misc/fancybox/images/blank.gif +0 -0
  97. data/plugins/misc/fancybox/images/fancy_close.png +0 -0
  98. data/plugins/misc/fancybox/images/fancy_loading.png +0 -0
  99. data/plugins/misc/fancybox/images/fancy_nav_left.png +0 -0
  100. data/plugins/misc/fancybox/images/fancy_nav_right.png +0 -0
  101. data/plugins/misc/fancybox/images/fancy_shadow_e.png +0 -0
  102. data/plugins/misc/fancybox/images/fancy_shadow_n.png +0 -0
  103. data/plugins/misc/fancybox/images/fancy_shadow_ne.png +0 -0
  104. data/plugins/misc/fancybox/images/fancy_shadow_nw.png +0 -0
  105. data/plugins/misc/fancybox/images/fancy_shadow_s.png +0 -0
  106. data/plugins/misc/fancybox/images/fancy_shadow_se.png +0 -0
  107. data/plugins/misc/fancybox/images/fancy_shadow_sw.png +0 -0
  108. data/plugins/misc/fancybox/images/fancy_shadow_w.png +0 -0
  109. data/plugins/misc/fancybox/images/fancy_title_left.png +0 -0
  110. data/plugins/misc/fancybox/images/fancy_title_main.png +0 -0
  111. data/plugins/misc/fancybox/images/fancy_title_over.png +0 -0
  112. data/plugins/misc/fancybox/images/fancy_title_right.png +0 -0
  113. data/plugins/misc/fancybox/images/fancybox-x.png +0 -0
  114. data/plugins/misc/fancybox/images/fancybox-y.png +0 -0
  115. data/plugins/misc/fancybox/images/fancybox.png +0 -0
  116. data/plugins/misc/fancybox/jquery.fancybox.css +1 -0
  117. data/plugins/misc/fancybox/jquery.fancybox.scss +323 -0
  118. data/plugins/misc/fancybox/main.rb +4 -0
  119. data/plugins/misc/fancybox/script.js +37 -0
  120. data/plugins/misc/fancybox/script/00-jquery.mousewheel.js +84 -0
  121. data/plugins/misc/fancybox/script/01-jquery.easing.js +205 -0
  122. data/plugins/misc/fancybox/script/02-jquery.fancybox.js +1156 -0
  123. data/plugins/misc/fancybox/script/init.js +18 -0
  124. data/plugins/misc/system.rb +192 -0
  125. data/plugins/misc/variables.rb +29 -0
  126. data/plugins/misc/webdav.rb +45 -0
  127. data/plugins/repositories/git_grep.rb +69 -0
  128. data/plugins/repositories/gitrb_repository.rb +204 -0
  129. data/plugins/repositories/locale.yml +12 -0
  130. data/plugins/repositories/rugged_repository.rb +454 -0
  131. data/plugins/security/acl.rb +57 -0
  132. data/plugins/security/basic_auth.rb +21 -0
  133. data/plugins/security/locale.yml +21 -0
  134. data/plugins/security/persistent_login.rb +32 -0
  135. data/plugins/security/portal.rb +28 -0
  136. data/plugins/security/private_wiki.rb +24 -0
  137. data/plugins/security/readonly_wiki.rb +25 -0
  138. data/plugins/security/stack.rb +20 -0
  139. data/plugins/security/yamlfile.rb +66 -0
  140. data/plugins/tags/code.rb +6 -0
  141. data/plugins/tags/footnotes.rb +35 -0
  142. data/plugins/tags/gist-embed.css +123 -0
  143. data/plugins/tags/gist.rb +13 -0
  144. data/plugins/tags/html.rb +57 -0
  145. data/plugins/tags/include.rb +20 -0
  146. data/plugins/tags/main.rb +353 -0
  147. data/plugins/tags/math.rb +117 -0
  148. data/plugins/tags/scripting.rb +64 -0
  149. data/plugins/tags/sort.rb +7 -0
  150. data/plugins/tags/tabs.rb +20 -0
  151. data/plugins/treeview/images/collapsed.png +0 -0
  152. data/plugins/treeview/images/expanded.png +0 -0
  153. data/plugins/treeview/images/menu.png +0 -0
  154. data/plugins/treeview/images/tree.png +0 -0
  155. data/plugins/treeview/images/wait.gif +0 -0
  156. data/plugins/treeview/main.rb +16 -0
  157. data/plugins/treeview/script.js +5 -0
  158. data/plugins/treeview/script/00-jquery.treeview.js +164 -0
  159. data/plugins/treeview/script/init.js +25 -0
  160. data/plugins/treeview/treeview.css +1 -0
  161. data/plugins/treeview/treeview.scss +113 -0
  162. data/plugins/utils/assets.rb +74 -0
  163. data/plugins/utils/cache.rb +53 -0
  164. data/plugins/utils/image_magick.rb +34 -0
  165. data/plugins/utils/pygments.css +1 -0
  166. data/plugins/utils/pygments.rb +50 -0
  167. data/plugins/utils/pygments.scss +83 -0
  168. data/plugins/utils/semaphore.rb +50 -0
  169. data/plugins/utils/shell.rb +45 -0
  170. data/plugins/utils/store.rb +315 -0
  171. data/plugins/utils/worker.rb +36 -0
  172. data/plugins/utils/xml.rb +29 -0
  173. data/static/images/favicon.png +0 -0
  174. data/static/script.js +267 -0
  175. data/static/script/00-json2.js +486 -0
  176. data/static/script/01-jstorage.js +217 -0
  177. data/static/script/02-jquery.js +9440 -0
  178. data/static/script/03-jquery.ui.core.js +337 -0
  179. data/static/script/04-jquery.ui.widget.js +502 -0
  180. data/static/script/05-jquery.ui.position.js +517 -0
  181. data/static/script/06-jquery.ui.menu.js +609 -0
  182. data/static/script/07-jquery.ui.autocomplete.js +601 -0
  183. data/static/script/08-olelo.i18n.js +37 -0
  184. data/static/script/09-olelo.unsaved.js +68 -0
  185. data/static/script/10-olelo.historytable.js +40 -0
  186. data/static/script/11-olelo.pagination.js +18 -0
  187. data/static/script/13-olelo.tabwidget.js +57 -0
  188. data/static/script/14-olelo.timeago.js +70 -0
  189. data/static/script/15-olelo.underliner.js +31 -0
  190. data/static/script/16-olelo.ui.combobox.js +32 -0
  191. data/static/script/init.js +48 -0
  192. data/static/themes/atlantis/constants.scss +15 -0
  193. data/static/themes/atlantis/iehacks.scss +38 -0
  194. data/static/themes/atlantis/images/actions/delete.png +0 -0
  195. data/static/themes/atlantis/images/actions/edit.png +0 -0
  196. data/static/themes/atlantis/images/actions/history.png +0 -0
  197. data/static/themes/atlantis/images/actions/home.png +0 -0
  198. data/static/themes/atlantis/images/actions/move.png +0 -0
  199. data/static/themes/atlantis/images/actions/new.png +0 -0
  200. data/static/themes/atlantis/images/actions/page.png +0 -0
  201. data/static/themes/atlantis/images/bg/button.png +0 -0
  202. data/static/themes/atlantis/images/bg/container.png +0 -0
  203. data/static/themes/atlantis/images/bg/content.png +0 -0
  204. data/static/themes/atlantis/images/bg/footer.png +0 -0
  205. data/static/themes/atlantis/images/bg/header.jpg +0 -0
  206. data/static/themes/atlantis/images/bg/header.orig.jpg +0 -0
  207. data/static/themes/atlantis/images/bg/header_gray.jpg +0 -0
  208. data/static/themes/atlantis/images/bug.png +0 -0
  209. data/static/themes/atlantis/images/filetypes/7z.png +0 -0
  210. data/static/themes/atlantis/images/filetypes/_archive.png +0 -0
  211. data/static/themes/atlantis/images/filetypes/_audio.png +0 -0
  212. data/static/themes/atlantis/images/filetypes/_code.png +0 -0
  213. data/static/themes/atlantis/images/filetypes/_linux.png +0 -0
  214. data/static/themes/atlantis/images/filetypes/_picture.png +0 -0
  215. data/static/themes/atlantis/images/filetypes/_video.png +0 -0
  216. data/static/themes/atlantis/images/filetypes/bz2.png +0 -0
  217. data/static/themes/atlantis/images/filetypes/doc.png +0 -0
  218. data/static/themes/atlantis/images/filetypes/flac.png +0 -0
  219. data/static/themes/atlantis/images/filetypes/gz.png +0 -0
  220. data/static/themes/atlantis/images/filetypes/html.png +0 -0
  221. data/static/themes/atlantis/images/filetypes/java.png +0 -0
  222. data/static/themes/atlantis/images/filetypes/jpg.png +0 -0
  223. data/static/themes/atlantis/images/filetypes/midi.png +0 -0
  224. data/static/themes/atlantis/images/filetypes/mp3.png +0 -0
  225. data/static/themes/atlantis/images/filetypes/ogg.png +0 -0
  226. data/static/themes/atlantis/images/filetypes/pdf.png +0 -0
  227. data/static/themes/atlantis/images/filetypes/php.png +0 -0
  228. data/static/themes/atlantis/images/filetypes/png.png +0 -0
  229. data/static/themes/atlantis/images/filetypes/ppt.png +0 -0
  230. data/static/themes/atlantis/images/filetypes/psd.png +0 -0
  231. data/static/themes/atlantis/images/filetypes/rar.png +0 -0
  232. data/static/themes/atlantis/images/filetypes/rb.png +0 -0
  233. data/static/themes/atlantis/images/filetypes/sh.png +0 -0
  234. data/static/themes/atlantis/images/filetypes/tar.png +0 -0
  235. data/static/themes/atlantis/images/filetypes/txt.png +0 -0
  236. data/static/themes/atlantis/images/filetypes/wma.png +0 -0
  237. data/static/themes/atlantis/images/filetypes/xls.png +0 -0
  238. data/static/themes/atlantis/images/filetypes/zip.png +0 -0
  239. data/static/themes/atlantis/images/folder.png +0 -0
  240. data/static/themes/atlantis/images/folder_open.png +0 -0
  241. data/static/themes/atlantis/images/loading.gif +0 -0
  242. data/static/themes/atlantis/images/loading.xcf +0 -0
  243. data/static/themes/atlantis/images/not_found.png +0 -0
  244. data/static/themes/atlantis/images/page.png +0 -0
  245. data/static/themes/atlantis/images/search.png +0 -0
  246. data/static/themes/atlantis/layout.scss +115 -0
  247. data/static/themes/atlantis/menu.scss +99 -0
  248. data/static/themes/atlantis/print.scss +129 -0
  249. data/static/themes/atlantis/screen.scss +495 -0
  250. data/static/themes/atlantis/style.css +3 -0
  251. data/static/themes/lib/autocomplete.scss +39 -0
  252. data/static/themes/lib/headlines.scss +10 -0
  253. data/static/themes/lib/horizontal-list.scss +31 -0
  254. data/static/themes/lib/patch.scss +88 -0
  255. data/static/themes/lib/reset.scss +114 -0
  256. data/static/themes/lib/rounded.scss +46 -0
  257. data/static/themes/lib/shadow.scss +14 -0
  258. data/test/config_test.rb +28 -0
  259. data/test/factory_test.rb +29 -0
  260. data/test/hash_extensions_test.rb +16 -0
  261. data/test/helper.rb +38 -0
  262. data/test/hooks_test.rb +85 -0
  263. data/test/object_extensions_test.rb +20 -0
  264. data/test/page_test.rb +168 -0
  265. data/test/request_test.rb +166 -0
  266. data/test/string_extensions_test.rb +32 -0
  267. data/test/templates_test.rb +39 -0
  268. data/test/util_test.rb +71 -0
  269. data/views/changes.slim +22 -0
  270. data/views/compare.slim +8 -0
  271. data/views/delete.slim +9 -0
  272. data/views/deleted.slim +2 -0
  273. data/views/edit.slim +65 -0
  274. data/views/error.slim +6 -0
  275. data/views/history.slim +20 -0
  276. data/views/layout.slim +38 -0
  277. data/views/login.slim +37 -0
  278. data/views/move.slim +10 -0
  279. data/views/not_found.slim +6 -0
  280. data/views/profile.slim +26 -0
  281. data/views/show.slim +9 -0
  282. metadata +488 -0
@@ -0,0 +1,311 @@
1
+ module Olelo
2
+ class PatchParser
3
+ include Util
4
+
5
+ class Handler
6
+ include Util
7
+
8
+ def initialize!
9
+ end
10
+
11
+ def finalize!
12
+ end
13
+
14
+ def begin!(src, dst)
15
+ end
16
+
17
+ def end!
18
+ end
19
+
20
+ def binary!
21
+ end
22
+
23
+ def deleted!
24
+ end
25
+
26
+ def no_changes!
27
+ end
28
+
29
+ def separator!
30
+ end
31
+
32
+ def insertion!(line)
33
+ end
34
+
35
+ def deletion!(line)
36
+ end
37
+
38
+ def context!(line)
39
+ end
40
+
41
+ def line!(line)
42
+ case line[0..0]
43
+ when '@'
44
+ separator!
45
+ when '+'
46
+ insertion!(line[1..-1])
47
+ when '-'
48
+ deletion!(line[1..-1])
49
+ else
50
+ context!(line[1..-1])
51
+ end
52
+ end
53
+ end
54
+
55
+ class Debugger
56
+ def method_missing(name, *args)
57
+ puts "#{name}: #{args.inspect}"
58
+ end
59
+ end
60
+
61
+ class Adapter
62
+ def initialize(adapter)
63
+ @adapter = adapter
64
+ end
65
+
66
+ def method_missing(name, *args)
67
+ @adapter.each {|a| a.send(name, *args) }
68
+ end
69
+ end
70
+
71
+ class ChangeHandler < Handler
72
+ def end!
73
+ handle_change
74
+ end
75
+
76
+ def change!(deletion, insertion)
77
+ deletion!(deletion)
78
+ insertion!(insertion)
79
+ end
80
+
81
+ def line!(line)
82
+ ch = line[0..0]
83
+ case ch
84
+ when '@'
85
+ handle_change
86
+ separator!
87
+ when '+'
88
+ handle_change if @deletion && @first == '+'
89
+ @first ||= '+'
90
+ (@insertion ||= '') << line[1..-1] << "\n"
91
+ when '-'
92
+ handle_change if @insertion && @first == '-'
93
+ @first ||= '-'
94
+ (@deletion ||= '') << line[1..-1] << "\n"
95
+ when ' '
96
+ handle_change
97
+ context!(line[1..-1] + "\n")
98
+ end
99
+ end
100
+
101
+ private
102
+
103
+ def handle_change
104
+ if @insertion && @deletion
105
+ change!(@deletion, @insertion)
106
+ elsif @insertion
107
+ insertion!(@insertion)
108
+ elsif @deletion
109
+ deletion!(@deletion)
110
+ end
111
+ @insertion = @deletion = @first = nil
112
+ end
113
+ end
114
+
115
+ def self.parse(patch, *h)
116
+ h = h.size == 1 ? h[0] : Adapter.new(h)
117
+ src = dst = nil
118
+ state = :start
119
+ h.initialize!
120
+ patch.split("\n").each do |line|
121
+ case state
122
+ when :start
123
+ case line
124
+ when %r{^diff.* ("?a/.*"?) ("?b/.*"?)$}
125
+ src, dst = unescape_path($1), unescape_path($2)
126
+ state = :header
127
+ when /^\+\+\+ (.*)/
128
+ dst = unescape_path($1)
129
+ state = :header
130
+ when /^\-\-\- (.*)/
131
+ src = unescape_path($1)
132
+ state = :header
133
+ end
134
+ when :header
135
+ case line
136
+ when %r{^diff.* ("?a/.*"?) ("?b/.*"?)$}
137
+ a, b = $1, $2
138
+ h.begin!(src, dst)
139
+ dst ? h.no_changes! : h.deleted!
140
+ h.end!
141
+ src, dst = unescape_path(a), unescape_path(b)
142
+ when /^\+\+\+ (.*)/
143
+ dst = unescape_path($1)
144
+ when /^\-\-\- (.*)/
145
+ src = unescape_path($1)
146
+ when /^deleted file/
147
+ dst = nil
148
+ when /^Binary files (.*) and (.*) differ/
149
+ src, dst = unescape_path($1), unescape_path($2)
150
+ h.begin!(src, dst)
151
+ h.binary!
152
+ h.end!
153
+ state = :start
154
+ when /^@/
155
+ state = :body
156
+ h.begin!(src, dst)
157
+ end
158
+ when :body
159
+ case line
160
+ when %r{^diff.* ("?a/.*"?) ("?b/.*"?)$}
161
+ src, dst = unescape_path($1), unescape_path($2)
162
+ h.end!
163
+ state = :header
164
+ else
165
+ h.line!(line)
166
+ end
167
+ end
168
+ end
169
+ case state
170
+ when :header
171
+ h.begin!(src, dst)
172
+ dst ? h.no_changes! : h.deleted!
173
+ h.end!
174
+ when :body
175
+ h.end!
176
+ end
177
+ h.finalize!
178
+ h
179
+ end
180
+
181
+ def self.unescape_path(path)
182
+ path = unescape_backslash(path[1..-2]) if path.starts_with? '"'
183
+ path == '/dev/null' ? nil : path[2..-1]
184
+ end
185
+ end
186
+
187
+ class PatchSummary < PatchParser::Handler
188
+ attr_reader :html
189
+
190
+ def initialize(options = {})
191
+ @options = options
192
+ end
193
+
194
+ def initialize!
195
+ @html = %{<table class="patch-summary"><thead><tr><th>#{escape_html :summary.t}</th><th class="ins">+</th><th class="del">-</th></tr></thead><tbody>}
196
+ @file = 0
197
+ end
198
+
199
+ def finalize!
200
+ @html << %{<tr><td colspan="3">#{escape_html :no_changes.t}</td></tr>} if @file == 0
201
+ @html << "</tbody></table>"
202
+ end
203
+
204
+ def begin!(src, dst)
205
+ @src, @dst = src, dst
206
+ @ins = @del = 0
207
+ end
208
+
209
+ def end!
210
+ if @src && @dst
211
+ if @src == @dst
212
+ @html << %{<tr class="edit"><td class="name">#{link(escape_html @src)}</td><td class="ins">#{@ins}</td><td class="del">#{@del}</td></tr>}
213
+ else
214
+ text = "#{escape_html @src} &#8594; #{escape_html @dst}"
215
+ @html << %{<tr class="move"><td class="name">#{link text}</td><td class="ins">#{@ins}</td><td class="del">#{@del}</td></tr>}
216
+ end
217
+ elsif @src
218
+ @html << %{<tr class="delete"><td class="name">#{link(escape_html @src)}</td><td class="ins">#{@ins}</td><td class="del">#{@del}</td></tr>}
219
+ else
220
+ @html << %{<tr class="new"><td class="name">#{link(escape_html @dst)}</td><td class="ins">#{@ins}</td><td class="del">#{@del}</td></tr>}
221
+ end
222
+ @file += 1
223
+ end
224
+
225
+ def binary!
226
+ @ins = @del = '-'
227
+ end
228
+
229
+ def insertion!(line)
230
+ @ins += 1
231
+ end
232
+
233
+ def deletion!(line)
234
+ @del += 1
235
+ end
236
+
237
+ def link(text)
238
+ @options[:links] ? %{<a href="#patch-#{@file}">#{text}</a>} : text
239
+ end
240
+ end
241
+
242
+ class PatchFormatter < PatchParser::ChangeHandler
243
+ attr_reader :html
244
+
245
+ def initialize(options = {})
246
+ @options = options
247
+ end
248
+
249
+ def initialize!
250
+ @html = ''
251
+ @file = 0
252
+ end
253
+
254
+ def begin!(src, dst)
255
+ super
256
+ @html << '<table class="patch"'
257
+ @html << %{ id="patch-#{@file}"} if @options[:links]
258
+ if @options[:header]
259
+ @html << '><thead><tr class="'
260
+ if src && dst
261
+ if src == dst
262
+ @html << 'edit"><th>' << escape_html(src)
263
+ else
264
+ @html << 'move"><th>' << escape_html(src) << ' &#8594; ' << escape_html(dst)
265
+ end
266
+ elsif src
267
+ @html << 'delete"><th>' << escape_html(src)
268
+ else
269
+ @html << 'new"><th>' << escape_html(dst)
270
+ end
271
+ @html << '</th></tr></thead><tbody><tr><td><pre>'
272
+ else
273
+ @html << '><tbody><tr><td><pre>'
274
+ end
275
+ end
276
+
277
+ def end!
278
+ super
279
+ @html << '</pre></td></tr></tbody></table>'
280
+ @file += 1
281
+ end
282
+
283
+ def binary!
284
+ @html << escape_html(:binary_file.t)
285
+ end
286
+
287
+ def deleted!
288
+ @html << escape_html(:deleted.t)
289
+ end
290
+
291
+ def no_changes!
292
+ @html << escape_html(:no_changes.t)
293
+ end
294
+
295
+ def separator!
296
+ @html << '</pre></td></tr><tr><td><pre>'
297
+ end
298
+
299
+ def insertion!(text)
300
+ @html << '<ins>' << escape_html(text) << '</ins>'
301
+ end
302
+
303
+ def deletion!(text)
304
+ @html << '<del>' << escape_html(text) << '</del>'
305
+ end
306
+
307
+ def context!(text)
308
+ @html << escape_html(text)
309
+ end
310
+ end
311
+ end
@@ -0,0 +1,188 @@
1
+ module Olelo
2
+ # Olelo plugin system
3
+ class Plugin < Module
4
+ include Util
5
+ include Hooks
6
+
7
+ has_around_hooks :load
8
+
9
+ @loaded = {}
10
+ @failed = []
11
+ @disabled = []
12
+ @dir = ''
13
+
14
+ class<< self
15
+ attr_accessor :dir, :disabled
16
+
17
+ # Get failed plugins
18
+ attr_reader :failed
19
+
20
+ # Current plugin
21
+ def caller
22
+ last, stack = nil, []
23
+ Kernel.caller(1).each do |line|
24
+ if line =~ %r{^#{@dir}/(.+?)(?:\/main)?\.rb} && $1 != last
25
+ stack << @loaded[$1]
26
+ last = $1
27
+ end
28
+ end
29
+ stack
30
+ end
31
+
32
+ # Get loaded plugins
33
+ def loaded
34
+ @loaded.values
35
+ end
36
+
37
+ # Start plugins
38
+ # @return [void]
39
+ def start
40
+ @loaded.each_value {|plugin| plugin.start }
41
+ end
42
+
43
+ def register(path, plugin)
44
+ @loaded[path] = plugin
45
+ end
46
+
47
+ # Load plugins by path
48
+ #
49
+ # @param list List of plugin paths to load
50
+ # @return [Boolean] true if every plugin was loaded
51
+ def load(*list)
52
+ files = list.map {|path| [File.join(@dir, path, 'main.rb'), File.join(@dir, "#{path}.rb")] }.flatten.select {|file| File.file?(file) }
53
+ return false if files.empty?
54
+ files.inject(true) do |result,file|
55
+ path = File.basename(file) == 'main.rb' ? file[(@dir.size+1)..-9] : file[(@dir.size+1)..-4]
56
+ if @loaded.include?(path)
57
+ result
58
+ elsif @failed.include?(path) || !enabled?(path)
59
+ false
60
+ else
61
+ begin
62
+ new(path, file)
63
+ rescue Exception => ex
64
+ @failed << path
65
+ if LoadError === ex
66
+ Olelo.logger.warn "Plugin #{path} could not be loaded due to: #{ex.message} (Missing gem?)"
67
+ else
68
+ Olelo.logger.error "Plugin #{path} could not be loaded due to: #{ex.message}"
69
+ Olelo.logger.error ex
70
+ end
71
+ @loaded.delete(path)
72
+ false
73
+ end
74
+ end
75
+ end
76
+ end
77
+
78
+ # Load all plugins
79
+ def load_all
80
+ load(*Dir[File.join(@dir, '**', '*.rb')].map {|file| file[(@dir.size+1)..-4] })
81
+ end
82
+
83
+ # Check if plugin is enabled
84
+ #
85
+ # @param [String] plugin path
86
+ # @return [Boolean] true if enabled
87
+ #
88
+ def enabled?(path)
89
+ path.split('/').inject('') do |parent, x|
90
+ parent /= x
91
+ return false if disabled.include?(parent)
92
+ parent
93
+ end
94
+ true
95
+ end
96
+
97
+ def for(obj)
98
+ if Module === obj
99
+ names = obj.name.split('::')
100
+ mod = Object
101
+ names.map {|name| mod = mod.const_get(name) }.reverse.each do |mod|
102
+ return mod if Plugin === mod
103
+ end
104
+ elsif Proc === obj
105
+ return obj.binding.eval('PLUGIN')
106
+ else
107
+ raise 'Plugin cannot be found for #{obj}'
108
+ end
109
+ end
110
+ end
111
+
112
+ attr_reader :path, :file
113
+ attr_setter :description
114
+ attr_reader? :started
115
+
116
+ def initialize(path, file)
117
+ @path, @file = path, file
118
+ @started = false
119
+ @dependencies = Set.new
120
+ const_set(:PLUGIN, self)
121
+
122
+ with_hooks :load do
123
+ names = path.split('/')
124
+ names[0..-2].inject('') do |parent, x|
125
+ parent /= x
126
+ Plugin.load(parent)
127
+ parent
128
+ end
129
+
130
+ (0...names.length).inject(Plugin) do |mod, i|
131
+ elem = names[i].split('_').map(&:capitalize).join
132
+ if mod.local_const_defined?(elem)
133
+ mod.const_get(elem)
134
+ else
135
+ child = i == names.length - 1 ? self : Module.new
136
+ child.module_eval { include mod } if mod != Plugin
137
+ mod.const_set(elem, child)
138
+ end
139
+ end
140
+
141
+ Plugin.register(path, self)
142
+ module_eval(File.read(file), file)
143
+ Olelo.logger.debug("Plugin #{path} successfully loaded")
144
+ end
145
+ end
146
+
147
+ # Virtual filesystem used to load plugin assets
148
+ def virtual_fs
149
+ VirtualFS::Union.new(VirtualFS::Embedded.new(file),
150
+ VirtualFS::Native.new(File.dirname(file)))
151
+ end
152
+
153
+ # Start the plugin by calling the {#setup} method
154
+ #
155
+ # @return [Boolean] true for success
156
+ def start
157
+ return true if @started
158
+ module_eval(&@setup) if @setup
159
+ Olelo.logger.debug "Plugin #{path} successfully started"
160
+ @started = true
161
+ rescue Exception => ex
162
+ Olelo.logger.error "Plugin #{path} failed to start due to: #{ex.message}"
163
+ Olelo.logger.error ex
164
+ false
165
+ end
166
+
167
+ # Load specified plugins and fail with LoadError if dependencies are missing
168
+ #
169
+ # @param list List of plugin paths to load
170
+ # @return List of dependencies (plugin paths)
171
+ def dependencies(*list)
172
+ if !list.empty?
173
+ raise 'Plugin is already started' if started?
174
+ @dependencies.merge(list)
175
+ list.each do |dep|
176
+ raise(LoadError, "Could not load dependency #{dep} for #{path}") if !Plugin.load(dep)
177
+ end
178
+ end
179
+ @dependencies
180
+ end
181
+
182
+ def setup(&block)
183
+ @setup = block
184
+ end
185
+
186
+ private_class_method :new
187
+ end
188
+ end