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,225 @@
1
+ module Olelo
2
+ # Version object
3
+ class Version
4
+ attr_reader :id, :author, :date, :comment, :parents
5
+ attr_reader? :head
6
+
7
+ def initialize(id, author, date, comment, parents, head)
8
+ @id, @author, @date, @comment, @parents, @head = id, author, date, comment, parents, head
9
+ end
10
+
11
+ # Returns shortened unique version id
12
+ #
13
+ # @return [String] shortened version id
14
+ # @api public
15
+ def short
16
+ Version.short(id)
17
+ end
18
+
19
+ # Shortens given version id
20
+ #
21
+ # @param [String] long version id
22
+ # @return [String] shortened version id
23
+ # @api public
24
+ def self.short(id)
25
+ Repository.instance.short_version(id)
26
+ end
27
+
28
+ # Return version id
29
+ #
30
+ # @return [String]
31
+ # @api public
32
+ def to_s
33
+ id
34
+ end
35
+
36
+ # Check equality of versions
37
+ #
38
+ # @param [Version, String] other version
39
+ # @return [Boolean]
40
+ # @api public
41
+ def ==(other)
42
+ other.to_s == id
43
+ end
44
+
45
+ def cache_id
46
+ "#{@id}-#{@head}"
47
+ end
48
+ end
49
+
50
+ # Difference between versions
51
+ Diff = Struct.new(:from, :to, :patch)
52
+
53
+ # Abstract repository base
54
+ #
55
+ # This class should not be used directly. Use the {Page} class
56
+ # which is implemented on top of it.
57
+ #
58
+ # @abstract
59
+ class Repository
60
+ include Util
61
+ extend Factory
62
+
63
+ def self.instance
64
+ Thread.current[:olelo_repository] ||= self[Config['repository.type']].new(Config['repository'][Config['repository.type']])
65
+ end
66
+
67
+ # Wrap block in transaction
68
+ #
69
+ # Every write operation has to be wrapped in a transaction.
70
+ #
71
+ # @yield Transaction block
72
+ # @return [void]
73
+ # @api public
74
+ def transaction
75
+ raise NotImplementedError
76
+ end
77
+
78
+ # Set content of path
79
+ #
80
+ # This method can only be used within a {#transaction} block.
81
+ #
82
+ # @param [String] path
83
+ # @param [String, #read] content
84
+ # @return [void]
85
+ # @api public
86
+ def set_content(path, content)
87
+ raise NotImplementedError
88
+ end
89
+
90
+ # Set attributes of path
91
+ #
92
+ # This method can only be used within a {#transaction} block.
93
+ #
94
+ # @param [String] path
95
+ # @param [Hash] attributes
96
+ # @return [void]
97
+ # @api public
98
+ def set_attributes(path, attributes)
99
+ raise NotImplementedError
100
+ end
101
+
102
+ # Move path
103
+ #
104
+ # This method can only be used within a {#transaction} block.
105
+ #
106
+ # @param [String] path
107
+ # @param [String] destination
108
+ # @return [void]
109
+ def move(path, destination)
110
+ raise NotImplementedError
111
+ end
112
+
113
+ # Delete path
114
+ #
115
+ # This method can only be used within a {#transaction} block.
116
+ #
117
+ # @param [String] path
118
+ # @return [void]
119
+ def delete(path)
120
+ raise NotImplementedError
121
+ end
122
+
123
+ # Commit changes with a comment
124
+ #
125
+ # This method can only be used within a {#transaction} block.
126
+ #
127
+ # @param [String] comment
128
+ # @return [Version] New head version
129
+ # @api public
130
+ def commit(comment)
131
+ raise NotImplementedError
132
+ end
133
+
134
+ # Check if path exists
135
+ #
136
+ # @param [String] path
137
+ # @param [String, Version] version
138
+ # @return [Boolean]
139
+ # @api public
140
+ def path_exists?(path, version)
141
+ raise NotImplementedError
142
+ end
143
+
144
+ # Find version by version id
145
+ #
146
+ # Returns head version if no version is given
147
+ #
148
+ # @param [String] version
149
+ # @return [Version]
150
+ # @api public
151
+ def get_version(version = nil)
152
+ raise NotImplementedError
153
+ end
154
+
155
+ # Get history of path beginning with newest version
156
+ #
157
+ # @param [String] path
158
+ # @param [Integer] Number of versions to skip
159
+ # @param [Integer] Maximum number of versions to load
160
+ # @return [Array<Version>]
161
+ # @api public
162
+ def get_history(path, skip = nil, limit = nil)
163
+ raise NotImplementedError
164
+ end
165
+
166
+ # Get versions of path
167
+ #
168
+ # @param [String] path
169
+ # @param [String, Version] version
170
+ # @return [Array<Version>] Tuple: previous version, current version, next version
171
+ # @api public
172
+ def get_path_version(path, version)
173
+ raise NotImplementedError
174
+ end
175
+
176
+ # Get children of path
177
+ #
178
+ # @param [String] path
179
+ # @param [String, Version] version
180
+ # @return [Array<String>] Names of children
181
+ # @api public
182
+ def get_children(path, version)
183
+ raise NotImplementedError
184
+ end
185
+
186
+ # Get content
187
+ #
188
+ # @param [String] path
189
+ # @param [String, Version] version
190
+ # @return [String] content
191
+ # @api public
192
+ def get_content(path, version)
193
+ raise NotImplementedError
194
+ end
195
+
196
+ # Get attributes
197
+ #
198
+ # @param [String] path
199
+ # @param [String, Version] version
200
+ # @return [Hash] attribute hash
201
+ # @api public
202
+ def get_attributes(path, version)
203
+ raise NotImplementedError
204
+ end
205
+
206
+ # Difference between versions for path
207
+ #
208
+ # @param [String] path
209
+ # @param [Version, String] from
210
+ # @param [Version, String] to
211
+ # @api public
212
+ def diff(path, from, to)
213
+ raise NotImplementedError
214
+ end
215
+
216
+ # Shortens given version id
217
+ #
218
+ # @param [String] long version id
219
+ # @return [String] shortened version id
220
+ # @api public
221
+ def short_version(version)
222
+ version
223
+ end
224
+ end
225
+ end
@@ -0,0 +1,223 @@
1
+ module Olelo
2
+ module Routing
3
+ def self.included(base)
4
+ base.extend(ClassMethods)
5
+ end
6
+
7
+ attr_reader :params, :original_params, :response, :request, :env
8
+
9
+ # Process rack request
10
+ #
11
+ # This method duplicates the object and calls {#call!} on it.
12
+ #
13
+ # @api public
14
+ # @param [Hash] env Rack environment
15
+ # @return [Array] Rack return value
16
+ # @see http://rack.rubyforge.org/doc/SPEC.html
17
+ def call(env)
18
+ dup.call!(env)
19
+ end
20
+
21
+ # Process rack request
22
+ #
23
+ # @api public
24
+ # @param [Hash] env Rack environment
25
+ # @return [Array] Rack return value
26
+ def call!(env)
27
+ @env = env
28
+ @request = Rack::Request.new(env)
29
+ @response = Rack::Response.new
30
+ @params = @original_params = @request.params.with_indifferent_access
31
+ @original_params.freeze
32
+
33
+ catch(:forward) do
34
+ with_hooks(:request) { perform! }
35
+ status, header, body = response.finish
36
+ return [status, header, request.head? ? [] : body]
37
+ end
38
+
39
+ @app ? @app.call(env) : error!(NotFound.new(@request.path_info))
40
+ end
41
+
42
+ # Halt routing with response
43
+ #
44
+ # Possible responses:
45
+ # * String or Object with #each
46
+ # * Symbol
47
+ # * [Symbol, String or Object with #each]
48
+ #
49
+ # @param [Symbol, String, #each] *response
50
+ # @return [void]
51
+ # @api public
52
+ def halt(*response)
53
+ throw :halt, response.length == 1 ? response.first : response
54
+ end
55
+
56
+ # Redirect to uri
57
+ #
58
+ # @param uri Target uri
59
+ # @return [void]
60
+ # @api public
61
+ def redirect(uri)
62
+ throw :redirect, uri
63
+ end
64
+
65
+ # Pass to next matching route
66
+ #
67
+ # @return [void]
68
+ # @api public
69
+ def pass
70
+ throw :pass
71
+ end
72
+
73
+ # Forward to next application on the rack stack
74
+ #
75
+ # @return [void]
76
+ # @api public
77
+ def forward
78
+ throw :forward
79
+ end
80
+
81
+ private
82
+
83
+ def error!(error)
84
+ response.status = Rack::Utils.status_code(error.try(:status) || :internal_server_error)
85
+ handle_error(error)
86
+ end
87
+
88
+ def perform!
89
+ result = catch(:halt) do
90
+ uri = catch(:redirect) do
91
+ with_hooks(:routing) { route! }
92
+ end
93
+ response.redirect uri
94
+ nil
95
+ end
96
+
97
+ return if !result
98
+ if result.respond_to?(:to_str)
99
+ response.body = [result]
100
+ elsif result.respond_to?(:to_ary)
101
+ result = result.to_ary
102
+ if result.length == 2 && Symbol === result.first
103
+ response.status = Rack::Utils.status_code(result.first)
104
+ response.body = result.last
105
+ else
106
+ response.body = result
107
+ end
108
+ elsif result.respond_to?(:each)
109
+ response.body = result
110
+ elsif Symbol === result
111
+ response.status = Rack::Utils.status_code(result)
112
+ else
113
+ raise TypeError, "#{result.inspect} not supported"
114
+ end
115
+ end
116
+
117
+ def route!
118
+ path = unescape(request.path_info)
119
+ method = request.request_method
120
+ self.class.router[method].find(path) do |name, params, function|
121
+ @params = @original_params.merge(params)
122
+ catch(:pass) do
123
+ with_hooks(:action, method.downcase.to_sym, name) do
124
+ halt function.bind(self).call
125
+ end
126
+ end
127
+ end if self.class.router[method]
128
+ raise NotFound, path
129
+ rescue ::Exception => error
130
+ halt error!(error)
131
+ end
132
+
133
+ class Router
134
+ SYNTAX = {
135
+ '\(' => '(?:', '\)' => ')?',
136
+ '\{' => '(?:', '\}' => ')',
137
+ '\|' => '|'
138
+ }.freeze
139
+
140
+ include Enumerable
141
+ attr_reader :head, :tail
142
+
143
+ def initialize
144
+ @head, @tail = [], []
145
+ end
146
+
147
+ def find(path)
148
+ each do |name, pattern, keys, function|
149
+ if match = pattern.match(path)
150
+ params = {}
151
+ keys.zip(match.captures.to_a).each {|k, v| params[k] = v if !v.blank? }
152
+ yield(name, params, function)
153
+ end
154
+ end
155
+ end
156
+
157
+ def each(&block)
158
+ @head.each(&block)
159
+ @tail.each(&block)
160
+ end
161
+
162
+ def add(function, path, patterns = {})
163
+ tail = patterns.delete(:tail)
164
+ pattern = Regexp.escape(path)
165
+ SYNTAX.each_pair {|k,v| pattern.gsub!(k, v) }
166
+ keys = []
167
+ pattern.gsub!(/:(\w+)/) do
168
+ keys << $1
169
+ patterns.key?($1) ? "(#{patterns[$1]})" : "([^/?&#\.]+)"
170
+ end
171
+ pattern = /^#{pattern}$/
172
+
173
+ if i = @head.index {|x| x.first == path }
174
+ @head[i] = [path, pattern, keys, function]
175
+ elsif i = @tail.index {|x| x.first == path }
176
+ @tail[i] = [path, pattern, keys, function]
177
+ else
178
+ (tail ? @tail : @head) << [path, pattern, keys, function]
179
+ end
180
+ end
181
+ end
182
+
183
+ module ClassMethods
184
+ def router
185
+ @router ||= {}
186
+ end
187
+
188
+ def patterns(patterns = nil)
189
+ @patterns ||= Hash.with_indifferent_access
190
+ patterns ? @patterns.merge!(patterns) : @patterns
191
+ end
192
+
193
+ def get(path, patterns = {}, &block)
194
+ add_route('GET', path, patterns, &block)
195
+ add_route('HEAD', path, patterns, &block)
196
+ end
197
+
198
+ def put(path, patterns = {}, &block)
199
+ add_route('PUT', path, patterns, &block)
200
+ end
201
+
202
+ def post(path, patterns = {}, &block)
203
+ add_route('POST', path, patterns, &block)
204
+ end
205
+
206
+ def delete(path, patterns = {}, &block)
207
+ add_route('DELETE', path, patterns, &block)
208
+ end
209
+
210
+ private
211
+
212
+ def add_route(method, path, patterns = {}, &block)
213
+ name = "#{method} #{path}"
214
+ if method_defined?(name)
215
+ redefine_method(name, &block)
216
+ else
217
+ define_method(name, &block)
218
+ end
219
+ (router[method] ||= Router.new).add(instance_method(name), path, self.patterns.merge(patterns))
220
+ end
221
+ end
222
+ end
223
+ end
@@ -0,0 +1,30 @@
1
+ module Olelo
2
+ module Templates
3
+ class << self
4
+ attr_reader :cache
5
+ attr_accessor :loader
6
+
7
+ def enable_caching
8
+ @cache = {}
9
+ end
10
+
11
+ def with_caching(id)
12
+ return cache[id] if cache && cache[id]
13
+ template = yield
14
+ cache[id] = template if cache
15
+ template
16
+ end
17
+ end
18
+
19
+ def render(name, options = {}, &block)
20
+ locals = options.delete(:locals) || {}
21
+ name = "#{name}.slim"
22
+ path = Templates.loader.context.to_s/name
23
+ id = [path, options.to_a].flatten.join('-')
24
+ template = Templates.with_caching(id) do
25
+ Slim::Template.new(path, options) { Templates.loader.load(name) }
26
+ end
27
+ template.render(self, locals, &block).html_safe
28
+ end
29
+ end
30
+ end