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.
- data/.gitignore +10 -0
- data/.gitmodules +3 -0
- data/Gemfile +3 -0
- data/README.markdown +104 -0
- data/Rakefile +92 -0
- data/bin/olelo +5 -0
- data/config.ru +86 -0
- data/config/aspects.rb +256 -0
- data/config/config.yml.default +154 -0
- data/config/initializers/00-mime_types.rb +29 -0
- data/config/initializers/01-slim.rb +2 -0
- data/config/interwiki.yml +11 -0
- data/doc/AUTHORS +7 -0
- data/doc/LICENSE +22 -0
- data/lib/olelo.rb +39 -0
- data/lib/olelo/application.rb +294 -0
- data/lib/olelo/attributes.rb +285 -0
- data/lib/olelo/config.rb +88 -0
- data/lib/olelo/extensions.rb +252 -0
- data/lib/olelo/helper.rb +290 -0
- data/lib/olelo/hooks.rb +142 -0
- data/lib/olelo/html_safe.rb +29 -0
- data/lib/olelo/initializer.rb +76 -0
- data/lib/olelo/locale.rb +63 -0
- data/lib/olelo/locale.yml +284 -0
- data/lib/olelo/menu.rb +101 -0
- data/lib/olelo/middleware/blacklist.rb +25 -0
- data/lib/olelo/middleware/degrade_mime_type.rb +19 -0
- data/lib/olelo/middleware/flash.rb +97 -0
- data/lib/olelo/middleware/force_encoding.rb +41 -0
- data/lib/olelo/page.rb +266 -0
- data/lib/olelo/patch.rb +311 -0
- data/lib/olelo/plugin.rb +188 -0
- data/lib/olelo/repository.rb +225 -0
- data/lib/olelo/routing.rb +223 -0
- data/lib/olelo/templates.rb +30 -0
- data/lib/olelo/user.rb +132 -0
- data/lib/olelo/util.rb +233 -0
- data/lib/olelo/version.rb +3 -0
- data/lib/olelo/virtualfs.rb +161 -0
- data/lib/rack/olelo_patches.rb +33 -0
- data/lib/rack/relative_redirect.rb +44 -0
- data/lib/rack/static_cache.rb +93 -0
- data/lib/yard/addons.rb +1 -0
- data/lib/yard/addons/hook_handler.rb +25 -0
- data/lib/yard/addons/override_tag.rb +14 -0
- data/lib/yard/addons/route_handler.rb +33 -0
- data/lib/yard/addons/sanitize_anchor.rb +16 -0
- data/olelo.gemspec +31 -0
- data/plugins/aspects/changelog.rb +45 -0
- data/plugins/aspects/documentbrowser.rb +57 -0
- data/plugins/aspects/download.rb +11 -0
- data/plugins/aspects/highlight.rb +8 -0
- data/plugins/aspects/image.rb +41 -0
- data/plugins/aspects/imageinfo.rb +64 -0
- data/plugins/aspects/locale.yml +60 -0
- data/plugins/aspects/main.rb +199 -0
- data/plugins/aspects/pageinfo.rb +37 -0
- data/plugins/aspects/source.rb +6 -0
- data/plugins/aspects/subpages.rb +44 -0
- data/plugins/aspects/text.rb +9 -0
- data/plugins/blog/blog.css +1 -0
- data/plugins/blog/blog.scss +37 -0
- data/plugins/blog/locale.yml +12 -0
- data/plugins/blog/main.rb +85 -0
- data/plugins/editor/locale.yml +18 -0
- data/plugins/editor/markup/main.rb +3 -0
- data/plugins/editor/markup/script.js +10 -0
- data/plugins/editor/markup/script/00-jquery.textselection.js +267 -0
- data/plugins/editor/markup/script/01-olelo.markupeditor.js +116 -0
- data/plugins/editor/markup/script/init.js +10 -0
- data/plugins/editor/preview.rb +52 -0
- data/plugins/editor/recaptcha.rb +56 -0
- data/plugins/filters/creole.rb +37 -0
- data/plugins/filters/disposition.rb +9 -0
- data/plugins/filters/editsection.rb +67 -0
- data/plugins/filters/fix_img_tag.rb +16 -0
- data/plugins/filters/html_wrapper.rb +12 -0
- data/plugins/filters/interwiki.rb +19 -0
- data/plugins/filters/link_classifier.rb +26 -0
- data/plugins/filters/locale.yml +15 -0
- data/plugins/filters/main.rb +202 -0
- data/plugins/filters/markdown_nowiki.rb +15 -0
- data/plugins/filters/numbering.xsl +93 -0
- data/plugins/filters/orgmode.rb +6 -0
- data/plugins/filters/rubypants.rb +6 -0
- data/plugins/filters/s5/main.rb +32 -0
- data/plugins/filters/s5/s5.xsl +118 -0
- data/plugins/filters/tilt.rb +17 -0
- data/plugins/filters/toc.rb +50 -0
- data/plugins/filters/xhtml2latex.xsl +232 -0
- data/plugins/filters/xslt.rb +22 -0
- data/plugins/gallery/gallery.css +1 -0
- data/plugins/gallery/gallery.scss +28 -0
- data/plugins/gallery/main.rb +34 -0
- data/plugins/misc/fancybox/images/blank.gif +0 -0
- data/plugins/misc/fancybox/images/fancy_close.png +0 -0
- data/plugins/misc/fancybox/images/fancy_loading.png +0 -0
- data/plugins/misc/fancybox/images/fancy_nav_left.png +0 -0
- data/plugins/misc/fancybox/images/fancy_nav_right.png +0 -0
- data/plugins/misc/fancybox/images/fancy_shadow_e.png +0 -0
- data/plugins/misc/fancybox/images/fancy_shadow_n.png +0 -0
- data/plugins/misc/fancybox/images/fancy_shadow_ne.png +0 -0
- data/plugins/misc/fancybox/images/fancy_shadow_nw.png +0 -0
- data/plugins/misc/fancybox/images/fancy_shadow_s.png +0 -0
- data/plugins/misc/fancybox/images/fancy_shadow_se.png +0 -0
- data/plugins/misc/fancybox/images/fancy_shadow_sw.png +0 -0
- data/plugins/misc/fancybox/images/fancy_shadow_w.png +0 -0
- data/plugins/misc/fancybox/images/fancy_title_left.png +0 -0
- data/plugins/misc/fancybox/images/fancy_title_main.png +0 -0
- data/plugins/misc/fancybox/images/fancy_title_over.png +0 -0
- data/plugins/misc/fancybox/images/fancy_title_right.png +0 -0
- data/plugins/misc/fancybox/images/fancybox-x.png +0 -0
- data/plugins/misc/fancybox/images/fancybox-y.png +0 -0
- data/plugins/misc/fancybox/images/fancybox.png +0 -0
- data/plugins/misc/fancybox/jquery.fancybox.css +1 -0
- data/plugins/misc/fancybox/jquery.fancybox.scss +323 -0
- data/plugins/misc/fancybox/main.rb +4 -0
- data/plugins/misc/fancybox/script.js +37 -0
- data/plugins/misc/fancybox/script/00-jquery.mousewheel.js +84 -0
- data/plugins/misc/fancybox/script/01-jquery.easing.js +205 -0
- data/plugins/misc/fancybox/script/02-jquery.fancybox.js +1156 -0
- data/plugins/misc/fancybox/script/init.js +18 -0
- data/plugins/misc/system.rb +192 -0
- data/plugins/misc/variables.rb +29 -0
- data/plugins/misc/webdav.rb +45 -0
- data/plugins/repositories/git_grep.rb +69 -0
- data/plugins/repositories/gitrb_repository.rb +204 -0
- data/plugins/repositories/locale.yml +12 -0
- data/plugins/repositories/rugged_repository.rb +454 -0
- data/plugins/security/acl.rb +57 -0
- data/plugins/security/basic_auth.rb +21 -0
- data/plugins/security/locale.yml +21 -0
- data/plugins/security/persistent_login.rb +32 -0
- data/plugins/security/portal.rb +28 -0
- data/plugins/security/private_wiki.rb +24 -0
- data/plugins/security/readonly_wiki.rb +25 -0
- data/plugins/security/stack.rb +20 -0
- data/plugins/security/yamlfile.rb +66 -0
- data/plugins/tags/code.rb +6 -0
- data/plugins/tags/footnotes.rb +35 -0
- data/plugins/tags/gist-embed.css +123 -0
- data/plugins/tags/gist.rb +13 -0
- data/plugins/tags/html.rb +57 -0
- data/plugins/tags/include.rb +20 -0
- data/plugins/tags/main.rb +353 -0
- data/plugins/tags/math.rb +117 -0
- data/plugins/tags/scripting.rb +64 -0
- data/plugins/tags/sort.rb +7 -0
- data/plugins/tags/tabs.rb +20 -0
- data/plugins/treeview/images/collapsed.png +0 -0
- data/plugins/treeview/images/expanded.png +0 -0
- data/plugins/treeview/images/menu.png +0 -0
- data/plugins/treeview/images/tree.png +0 -0
- data/plugins/treeview/images/wait.gif +0 -0
- data/plugins/treeview/main.rb +16 -0
- data/plugins/treeview/script.js +5 -0
- data/plugins/treeview/script/00-jquery.treeview.js +164 -0
- data/plugins/treeview/script/init.js +25 -0
- data/plugins/treeview/treeview.css +1 -0
- data/plugins/treeview/treeview.scss +113 -0
- data/plugins/utils/assets.rb +74 -0
- data/plugins/utils/cache.rb +53 -0
- data/plugins/utils/image_magick.rb +34 -0
- data/plugins/utils/pygments.css +1 -0
- data/plugins/utils/pygments.rb +50 -0
- data/plugins/utils/pygments.scss +83 -0
- data/plugins/utils/semaphore.rb +50 -0
- data/plugins/utils/shell.rb +45 -0
- data/plugins/utils/store.rb +315 -0
- data/plugins/utils/worker.rb +36 -0
- data/plugins/utils/xml.rb +29 -0
- data/static/images/favicon.png +0 -0
- data/static/script.js +267 -0
- data/static/script/00-json2.js +486 -0
- data/static/script/01-jstorage.js +217 -0
- data/static/script/02-jquery.js +9440 -0
- data/static/script/03-jquery.ui.core.js +337 -0
- data/static/script/04-jquery.ui.widget.js +502 -0
- data/static/script/05-jquery.ui.position.js +517 -0
- data/static/script/06-jquery.ui.menu.js +609 -0
- data/static/script/07-jquery.ui.autocomplete.js +601 -0
- data/static/script/08-olelo.i18n.js +37 -0
- data/static/script/09-olelo.unsaved.js +68 -0
- data/static/script/10-olelo.historytable.js +40 -0
- data/static/script/11-olelo.pagination.js +18 -0
- data/static/script/13-olelo.tabwidget.js +57 -0
- data/static/script/14-olelo.timeago.js +70 -0
- data/static/script/15-olelo.underliner.js +31 -0
- data/static/script/16-olelo.ui.combobox.js +32 -0
- data/static/script/init.js +48 -0
- data/static/themes/atlantis/constants.scss +15 -0
- data/static/themes/atlantis/iehacks.scss +38 -0
- data/static/themes/atlantis/images/actions/delete.png +0 -0
- data/static/themes/atlantis/images/actions/edit.png +0 -0
- data/static/themes/atlantis/images/actions/history.png +0 -0
- data/static/themes/atlantis/images/actions/home.png +0 -0
- data/static/themes/atlantis/images/actions/move.png +0 -0
- data/static/themes/atlantis/images/actions/new.png +0 -0
- data/static/themes/atlantis/images/actions/page.png +0 -0
- data/static/themes/atlantis/images/bg/button.png +0 -0
- data/static/themes/atlantis/images/bg/container.png +0 -0
- data/static/themes/atlantis/images/bg/content.png +0 -0
- data/static/themes/atlantis/images/bg/footer.png +0 -0
- data/static/themes/atlantis/images/bg/header.jpg +0 -0
- data/static/themes/atlantis/images/bg/header.orig.jpg +0 -0
- data/static/themes/atlantis/images/bg/header_gray.jpg +0 -0
- data/static/themes/atlantis/images/bug.png +0 -0
- data/static/themes/atlantis/images/filetypes/7z.png +0 -0
- data/static/themes/atlantis/images/filetypes/_archive.png +0 -0
- data/static/themes/atlantis/images/filetypes/_audio.png +0 -0
- data/static/themes/atlantis/images/filetypes/_code.png +0 -0
- data/static/themes/atlantis/images/filetypes/_linux.png +0 -0
- data/static/themes/atlantis/images/filetypes/_picture.png +0 -0
- data/static/themes/atlantis/images/filetypes/_video.png +0 -0
- data/static/themes/atlantis/images/filetypes/bz2.png +0 -0
- data/static/themes/atlantis/images/filetypes/doc.png +0 -0
- data/static/themes/atlantis/images/filetypes/flac.png +0 -0
- data/static/themes/atlantis/images/filetypes/gz.png +0 -0
- data/static/themes/atlantis/images/filetypes/html.png +0 -0
- data/static/themes/atlantis/images/filetypes/java.png +0 -0
- data/static/themes/atlantis/images/filetypes/jpg.png +0 -0
- data/static/themes/atlantis/images/filetypes/midi.png +0 -0
- data/static/themes/atlantis/images/filetypes/mp3.png +0 -0
- data/static/themes/atlantis/images/filetypes/ogg.png +0 -0
- data/static/themes/atlantis/images/filetypes/pdf.png +0 -0
- data/static/themes/atlantis/images/filetypes/php.png +0 -0
- data/static/themes/atlantis/images/filetypes/png.png +0 -0
- data/static/themes/atlantis/images/filetypes/ppt.png +0 -0
- data/static/themes/atlantis/images/filetypes/psd.png +0 -0
- data/static/themes/atlantis/images/filetypes/rar.png +0 -0
- data/static/themes/atlantis/images/filetypes/rb.png +0 -0
- data/static/themes/atlantis/images/filetypes/sh.png +0 -0
- data/static/themes/atlantis/images/filetypes/tar.png +0 -0
- data/static/themes/atlantis/images/filetypes/txt.png +0 -0
- data/static/themes/atlantis/images/filetypes/wma.png +0 -0
- data/static/themes/atlantis/images/filetypes/xls.png +0 -0
- data/static/themes/atlantis/images/filetypes/zip.png +0 -0
- data/static/themes/atlantis/images/folder.png +0 -0
- data/static/themes/atlantis/images/folder_open.png +0 -0
- data/static/themes/atlantis/images/loading.gif +0 -0
- data/static/themes/atlantis/images/loading.xcf +0 -0
- data/static/themes/atlantis/images/not_found.png +0 -0
- data/static/themes/atlantis/images/page.png +0 -0
- data/static/themes/atlantis/images/search.png +0 -0
- data/static/themes/atlantis/layout.scss +115 -0
- data/static/themes/atlantis/menu.scss +99 -0
- data/static/themes/atlantis/print.scss +129 -0
- data/static/themes/atlantis/screen.scss +495 -0
- data/static/themes/atlantis/style.css +3 -0
- data/static/themes/lib/autocomplete.scss +39 -0
- data/static/themes/lib/headlines.scss +10 -0
- data/static/themes/lib/horizontal-list.scss +31 -0
- data/static/themes/lib/patch.scss +88 -0
- data/static/themes/lib/reset.scss +114 -0
- data/static/themes/lib/rounded.scss +46 -0
- data/static/themes/lib/shadow.scss +14 -0
- data/test/config_test.rb +28 -0
- data/test/factory_test.rb +29 -0
- data/test/hash_extensions_test.rb +16 -0
- data/test/helper.rb +38 -0
- data/test/hooks_test.rb +85 -0
- data/test/object_extensions_test.rb +20 -0
- data/test/page_test.rb +168 -0
- data/test/request_test.rb +166 -0
- data/test/string_extensions_test.rb +32 -0
- data/test/templates_test.rb +39 -0
- data/test/util_test.rb +71 -0
- data/views/changes.slim +22 -0
- data/views/compare.slim +8 -0
- data/views/delete.slim +9 -0
- data/views/deleted.slim +2 -0
- data/views/edit.slim +65 -0
- data/views/error.slim +6 -0
- data/views/history.slim +20 -0
- data/views/layout.slim +38 -0
- data/views/login.slim +37 -0
- data/views/move.slim +10 -0
- data/views/not_found.slim +6 -0
- data/views/profile.slim +26 -0
- data/views/show.slim +9 -0
- metadata +488 -0
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
module Olelo
|
|
2
|
+
# Include module to add attribute editor to a class.
|
|
3
|
+
module Attributes
|
|
4
|
+
def self.included(base)
|
|
5
|
+
base.extend(ClassMethods)
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
# Attribute data structure
|
|
9
|
+
# @api private
|
|
10
|
+
class Attribute
|
|
11
|
+
include Util
|
|
12
|
+
|
|
13
|
+
# @api private
|
|
14
|
+
attr_reader :key, :name
|
|
15
|
+
|
|
16
|
+
def initialize(parent, name)
|
|
17
|
+
@name = name.to_s
|
|
18
|
+
@key = ['attribute', parent.path, name].compact.join('_')
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def label
|
|
22
|
+
@label ||= Locale.translate(key, :fallback => titlecase(name))
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def label_tag
|
|
26
|
+
type = self.class.name.split('::').last.downcase
|
|
27
|
+
title = Locale.translate("type_#{type}", :fallback => titlecase(type))
|
|
28
|
+
%{<label for="#{key}" title="#{escape_html title}">#{escape_html label}</label>}
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def build_form(attr)
|
|
32
|
+
"#{label_tag}#{field_tag(attr)}<br/>"
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
class String < Attribute
|
|
36
|
+
def field_tag(attr)
|
|
37
|
+
%{<input class="observe" type="text" id="#{key}" name="#{key}" value="#{escape_html attr}"/>}
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def parse(params)
|
|
41
|
+
value = params[key]
|
|
42
|
+
value if !value.blank?
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
class List < Attribute
|
|
47
|
+
def field_tag(attr)
|
|
48
|
+
%{<input class="observe" type="text" id="#{key}" name="#{key}" value="#{escape_html attr.to_a.join(', ')}"/>}
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def parse(params)
|
|
52
|
+
value = params[key]
|
|
53
|
+
value.split(/\s*,\s*/) if !value.blank?
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
class Integer < Attribute
|
|
58
|
+
def field_tag(attr)
|
|
59
|
+
%{<input class="observe" type="text" id="#{key}" name="#{key}" value="#{escape_html attr}"/>}
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def parse(params)
|
|
63
|
+
value = params[key]
|
|
64
|
+
value.to_i if !value.blank?
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
class Boolean < Attribute
|
|
69
|
+
def field_tag(attr)
|
|
70
|
+
%{<input class="observe" type="checkbox" id="#{key}" name="#{key}" value="true"#{attr ? ' checked="checked"' : ''}/>}
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def build_form(attr)
|
|
74
|
+
"<div class=\"indent\">#{field_tag(attr)}#{label_tag}</div><br/>\n"
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def parse(params)
|
|
78
|
+
value = params[key]
|
|
79
|
+
true if value == 'true'
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
class Enum < Attribute
|
|
84
|
+
def initialize(parent, name, values = {})
|
|
85
|
+
super(parent, name)
|
|
86
|
+
raise 'Values must be Proc, Hash or Array' unless Proc === values || Hash === values || Array === values
|
|
87
|
+
@values = values
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def field_tag(attr)
|
|
91
|
+
html = %{<select class="observe" id="#{key}" name="#{key}">
|
|
92
|
+
<option#{values.any? {|value,label| attr == value} ? '' : ' selected="selected"'}></option>}
|
|
93
|
+
values.sort_by(&:last).each do |value,label|
|
|
94
|
+
value_attr = value == label ? '' : %{ value="#{escape_html value}"}
|
|
95
|
+
selected_attr = attr == value ? ' selected="selected"' : ''
|
|
96
|
+
html << %{<option#{value_attr}#{selected_attr}>#{escape_html label}</option>}
|
|
97
|
+
end
|
|
98
|
+
html << '</select>'
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def parse(params)
|
|
102
|
+
value = params[key]
|
|
103
|
+
value if values.include?(value)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
private
|
|
107
|
+
|
|
108
|
+
def values
|
|
109
|
+
if Proc === @values
|
|
110
|
+
@values = @values.call
|
|
111
|
+
raise 'Values must be Hash or Array' unless Hash === @values || Array === @values
|
|
112
|
+
end
|
|
113
|
+
@values = Hash[*@values.zip(@values)] if Array === @values
|
|
114
|
+
@values
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
class Suggestions < Enum
|
|
119
|
+
def field_tag(attr)
|
|
120
|
+
%{<input class="observe" type="text" id="#{key}" name="#{key}" value="#{escape_html(values[attr] || attr)}"/>
|
|
121
|
+
<script type="text/javascript">
|
|
122
|
+
$('##{key}').combobox({ source: #{escape_javascript values.values.sort.to_json} });
|
|
123
|
+
</script>}
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def parse(params)
|
|
127
|
+
value = params[key]
|
|
128
|
+
inverted_values[value] || value if !value.blank?
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def inverted_values
|
|
132
|
+
@inverted_values ||= values.invert
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
# Data structure for group of attributes
|
|
138
|
+
# @api private
|
|
139
|
+
class AttributeGroup
|
|
140
|
+
include Util
|
|
141
|
+
|
|
142
|
+
# @api private
|
|
143
|
+
attr_reader :name, :path, :children
|
|
144
|
+
|
|
145
|
+
def initialize(parent = nil, name = nil)
|
|
146
|
+
@name = name.to_s
|
|
147
|
+
@path = parent ? [parent.path, name].compact.join('_') : nil
|
|
148
|
+
@children = {}
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def label
|
|
152
|
+
@label ||= name.blank? ? '' : Locale.translate("group_#{path}", :fallback => titlecase(name))
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# Build form for this group
|
|
156
|
+
# @return [String] html
|
|
157
|
+
# @api private
|
|
158
|
+
#
|
|
159
|
+
def build_form(attr)
|
|
160
|
+
html = label.blank? ? '' : "<h3>#{escape_html label}</h3>\n"
|
|
161
|
+
html << children.sort_by do |name, child|
|
|
162
|
+
[Attribute === child ? 0 : 1, child.label]
|
|
163
|
+
end.map do |name, child|
|
|
164
|
+
child.build_form(attr ? attr[name] : nil)
|
|
165
|
+
end.join
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
# Parse params and return attribute hash for this group
|
|
169
|
+
# @return [Hash]
|
|
170
|
+
# @api private
|
|
171
|
+
#
|
|
172
|
+
def parse(params)
|
|
173
|
+
attr = {}
|
|
174
|
+
children.each_pair do |name, child|
|
|
175
|
+
value = child.parse(params)
|
|
176
|
+
attr[name] = value if value
|
|
177
|
+
end
|
|
178
|
+
attr.empty? ? nil : attr
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
# DSL class used to initialize AttributeGroup
|
|
183
|
+
class AttributeDSL
|
|
184
|
+
include Util
|
|
185
|
+
|
|
186
|
+
# Initialize DSL with `group`
|
|
187
|
+
#
|
|
188
|
+
# @param [Olelo::Attributes::AttributeGroup] AttributeGroup to modify in this DSL block
|
|
189
|
+
# @yield DSL block
|
|
190
|
+
#
|
|
191
|
+
def initialize(group, &block)
|
|
192
|
+
@group = group
|
|
193
|
+
instance_eval(&block)
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
def string(name, values = nil, &block)
|
|
197
|
+
@group.children[name.to_s] = if values || block
|
|
198
|
+
Attribute::Suggestions.new(@group, name, block ? block : values)
|
|
199
|
+
else
|
|
200
|
+
Attribute::String.new(@group, name)
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
def integer(name)
|
|
205
|
+
@group.children[name.to_s] = Attribute::Integer.new(@group, name)
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
def boolean(name)
|
|
209
|
+
@group.children[name.to_s] = Attribute::Boolean.new(@group, name)
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
def list(name)
|
|
213
|
+
@group.children[name.to_s] = Attribute::List.new(@group, name)
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
def enum(name, values = nil, &block)
|
|
217
|
+
@group.children[name.to_s] = Attribute::Enum.new(@group, name, block ? block : values)
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
# Define attribute group
|
|
221
|
+
#
|
|
222
|
+
# @yield DSL block
|
|
223
|
+
# @return [void]
|
|
224
|
+
# @api public
|
|
225
|
+
#
|
|
226
|
+
def group(name, &block)
|
|
227
|
+
AttributeDSL.new(@group.children[name.to_s] ||= AttributeGroup.new(@group, name), &block)
|
|
228
|
+
end
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
# Extends class with attribute editor DSL
|
|
232
|
+
module ClassMethods
|
|
233
|
+
# Root attribute group
|
|
234
|
+
#
|
|
235
|
+
# @return [AttributeGroup] Root editor group
|
|
236
|
+
# @api private
|
|
237
|
+
#
|
|
238
|
+
def attribute_group
|
|
239
|
+
@attribute_group ||= AttributeGroup.new
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
# Add attribute to the attribute editor
|
|
243
|
+
#
|
|
244
|
+
# @yield DSL block
|
|
245
|
+
# @return [void]
|
|
246
|
+
# @api public
|
|
247
|
+
#
|
|
248
|
+
# @example add string attribute title
|
|
249
|
+
# attributes do
|
|
250
|
+
# string :title
|
|
251
|
+
# end
|
|
252
|
+
#
|
|
253
|
+
# @example add group with multiple attributes
|
|
254
|
+
# attributes do
|
|
255
|
+
# group :acl do
|
|
256
|
+
# list :read
|
|
257
|
+
# list :write
|
|
258
|
+
# end
|
|
259
|
+
#
|
|
260
|
+
def attributes(&block)
|
|
261
|
+
AttributeDSL.new(attribute_group, &block)
|
|
262
|
+
end
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
# Parse attributes from params hash
|
|
266
|
+
#
|
|
267
|
+
# @param [Hash] params submitted params hash
|
|
268
|
+
# @return [Hash] Attributes
|
|
269
|
+
# @api public
|
|
270
|
+
#
|
|
271
|
+
def update_attributes(params)
|
|
272
|
+
self.attributes = self.class.attribute_group.parse(params)
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
# Generate attribute editor form
|
|
276
|
+
#
|
|
277
|
+
# @param [Hash] default_values to use for the form
|
|
278
|
+
# @return [String] Generated html form
|
|
279
|
+
# @api public
|
|
280
|
+
#
|
|
281
|
+
def attribute_editor
|
|
282
|
+
self.class.attribute_group.build_form(attributes).html_safe
|
|
283
|
+
end
|
|
284
|
+
end
|
|
285
|
+
end
|
data/lib/olelo/config.rb
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
module Olelo
|
|
2
|
+
class Config
|
|
3
|
+
include Enumerable
|
|
4
|
+
|
|
5
|
+
attr_reader :base, :hash
|
|
6
|
+
|
|
7
|
+
def initialize(base = nil)
|
|
8
|
+
@hash = {}
|
|
9
|
+
@base = base.freeze
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def [](key)
|
|
13
|
+
key = key.to_s
|
|
14
|
+
if i = key.index('.')
|
|
15
|
+
not_found(key) unless Config === hash[key[0...i]]
|
|
16
|
+
hash[key[0...i]][key[i+1..-1]]
|
|
17
|
+
else
|
|
18
|
+
not_found(key) if !hash.include?(key)
|
|
19
|
+
hash[key]
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def []=(key, value)
|
|
24
|
+
key = key.to_s
|
|
25
|
+
if i = key.index('.')
|
|
26
|
+
child(key[0...i])[key[i+1..-1]] = value
|
|
27
|
+
elsif Hash === value
|
|
28
|
+
child(key).update(value)
|
|
29
|
+
else
|
|
30
|
+
hash[key] = value.freeze
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def update(hash)
|
|
35
|
+
hash.each_pair do |key, value|
|
|
36
|
+
self[key] = value
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def load(file)
|
|
41
|
+
load!(file) if File.file?(file)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def load!(file)
|
|
45
|
+
update(YAML.load_file(file))
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def each(&block)
|
|
49
|
+
hash.each(&block)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def self.instance
|
|
53
|
+
@instance ||= Config.new
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def self.[](key)
|
|
57
|
+
instance[key]
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def to_hash
|
|
61
|
+
h = Hash.with_indifferent_access
|
|
62
|
+
hash.each_pair do |k, v|
|
|
63
|
+
h[k] = Config === v ? v.to_hash : v
|
|
64
|
+
end
|
|
65
|
+
h
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def freeze
|
|
69
|
+
hash.freeze
|
|
70
|
+
super
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
private
|
|
74
|
+
|
|
75
|
+
def not_found(key)
|
|
76
|
+
raise(NameError, "Configuration key #{base ? "#{base}.#{key}" : key} not found")
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def child(key)
|
|
80
|
+
if child = hash[key]
|
|
81
|
+
raise "Configuration key #{key} is already used" unless Config === child
|
|
82
|
+
child
|
|
83
|
+
else
|
|
84
|
+
hash[key] = Config.new(base ? "#{base}.#{key}" : key)
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
class Module
|
|
2
|
+
# Generate accessor method with question mark
|
|
3
|
+
#
|
|
4
|
+
# @param [String, Symbol...] attrs Attributes to generate setter for
|
|
5
|
+
# @return [void]
|
|
6
|
+
#
|
|
7
|
+
def attr_reader?(*attrs)
|
|
8
|
+
attrs.each do |a|
|
|
9
|
+
module_eval "def #{a}?; !!@#{a}; end"
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# Generate attribute setter
|
|
14
|
+
#
|
|
15
|
+
# A setter accepts an argument to
|
|
16
|
+
# set a value. It acts as a getter without an argument.
|
|
17
|
+
#
|
|
18
|
+
# @see Ruby facets
|
|
19
|
+
# @param [String, Symbol...] attrs Attributes to generate setter for
|
|
20
|
+
# @return [void]
|
|
21
|
+
#
|
|
22
|
+
def attr_setter(*attrs)
|
|
23
|
+
code, made = '', []
|
|
24
|
+
attrs.each do |a|
|
|
25
|
+
code << "def #{a}(*a); a.size > 0 ? (@#{a}=a[0]; self) : @#{a} end\n"
|
|
26
|
+
made << a.to_sym
|
|
27
|
+
end
|
|
28
|
+
module_eval(code)
|
|
29
|
+
made
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Redefine a module method
|
|
33
|
+
#
|
|
34
|
+
# Replaces alias_method_chain and allows to call
|
|
35
|
+
# overwritten method via super.
|
|
36
|
+
#
|
|
37
|
+
# @param [Symbol, String] name of method
|
|
38
|
+
# @yield New method block
|
|
39
|
+
# @return [void]
|
|
40
|
+
#
|
|
41
|
+
def redefine_method(name, &block)
|
|
42
|
+
if instance_methods(false).any? {|x| x.to_s == name.to_s }
|
|
43
|
+
method = instance_method(name)
|
|
44
|
+
mod = Module.new do
|
|
45
|
+
define_method(name) {|*args| method.bind(self).call(*args) }
|
|
46
|
+
end
|
|
47
|
+
remove_method(name)
|
|
48
|
+
include(mod)
|
|
49
|
+
end
|
|
50
|
+
include(Module.new { define_method(name, &block) })
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
if method(:const_defined?).arity == 1
|
|
54
|
+
# Ruby 1.8
|
|
55
|
+
def local_const_defined?(const)
|
|
56
|
+
const_defined?(const)
|
|
57
|
+
end
|
|
58
|
+
else
|
|
59
|
+
# Ruby 1.9
|
|
60
|
+
def local_const_defined?(const)
|
|
61
|
+
const_defined?(const, false)
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
class Hash
|
|
67
|
+
# Stolen from rails
|
|
68
|
+
class WithIndifferentAccess < Hash
|
|
69
|
+
def initialize(arg = {})
|
|
70
|
+
if Hash === arg
|
|
71
|
+
super()
|
|
72
|
+
update(arg)
|
|
73
|
+
else
|
|
74
|
+
super(arg)
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
alias_method :regular_include, :include?
|
|
79
|
+
alias_method :regular_writer, :[]=
|
|
80
|
+
alias_method :regular_update, :update
|
|
81
|
+
|
|
82
|
+
def default(key = nil)
|
|
83
|
+
if Symbol === key && regular_include(key = key.to_s)
|
|
84
|
+
self[key]
|
|
85
|
+
else
|
|
86
|
+
super
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def []=(key, value)
|
|
91
|
+
regular_writer(convert_key(key), value)
|
|
92
|
+
value
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def update(other)
|
|
96
|
+
other.each_pair {|key, value| regular_writer(convert_key(key), value) }
|
|
97
|
+
self
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
alias_method :merge!, :update
|
|
101
|
+
|
|
102
|
+
def key?(key)
|
|
103
|
+
super(convert_key(key))
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
alias_method :include?, :key?
|
|
107
|
+
alias_method :has_key?, :key?
|
|
108
|
+
alias_method :member?, :key?
|
|
109
|
+
|
|
110
|
+
def fetch(key, *extras)
|
|
111
|
+
super(convert_key(key), *extras)
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def values_at(*indices)
|
|
115
|
+
indices.collect {|key| self[convert_key(key)]}
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def dup
|
|
119
|
+
WithIndifferentAccess.new(self)
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def merge(hash)
|
|
123
|
+
self.dup.update(hash)
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def delete(key)
|
|
127
|
+
super(convert_key(key))
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def to_hash
|
|
131
|
+
Hash.new(default).merge(self)
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
protected
|
|
135
|
+
|
|
136
|
+
def convert_key(key)
|
|
137
|
+
Symbol === key ? key.to_s : key
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def with_indifferent_access
|
|
142
|
+
hash = WithIndifferentAccess.new(self)
|
|
143
|
+
hash.default = self.default
|
|
144
|
+
hash
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def self.with_indifferent_access(arg = {})
|
|
148
|
+
WithIndifferentAccess.new(arg)
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
class Object
|
|
153
|
+
# Metaclass of object
|
|
154
|
+
#
|
|
155
|
+
# @return [Class]
|
|
156
|
+
#
|
|
157
|
+
def metaclass
|
|
158
|
+
(class << self; self; end)
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
# Returns true if object is empty or false
|
|
162
|
+
#
|
|
163
|
+
# @return [Boolean]
|
|
164
|
+
#
|
|
165
|
+
def blank?
|
|
166
|
+
respond_to?(:empty?) ? empty? : !self
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
# Try to call method if it exists or return nil
|
|
170
|
+
#
|
|
171
|
+
# @param [String, Symbol] name Method name
|
|
172
|
+
# @param args Method arguments
|
|
173
|
+
# @return Method result or nil
|
|
174
|
+
#
|
|
175
|
+
def try(name, *args)
|
|
176
|
+
respond_to?(name) ? send(name, *args) : nil
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
class NilClass; def blank?; true; end; end
|
|
181
|
+
class FalseClass; def blank?; true; end; end
|
|
182
|
+
class TrueClass; def blank?; false; end; end
|
|
183
|
+
|
|
184
|
+
class String
|
|
185
|
+
# Faster blank?
|
|
186
|
+
alias blank? empty?
|
|
187
|
+
|
|
188
|
+
# Try to force encoding
|
|
189
|
+
#
|
|
190
|
+
# Force encoding of string and revert
|
|
191
|
+
# to original encoding if string has no valid encoding
|
|
192
|
+
#
|
|
193
|
+
# @param [Encoding, String] enc New encoding
|
|
194
|
+
# @return self
|
|
195
|
+
#
|
|
196
|
+
def try_encoding(enc)
|
|
197
|
+
old_enc = encoding
|
|
198
|
+
if old_enc != enc
|
|
199
|
+
force_encoding(enc)
|
|
200
|
+
force_encoding(old_enc) if !valid_encoding?
|
|
201
|
+
end
|
|
202
|
+
self
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
# Check if string starts with s
|
|
206
|
+
#
|
|
207
|
+
# @param [String] s
|
|
208
|
+
# @return [Boolean]
|
|
209
|
+
#
|
|
210
|
+
def starts_with?(s)
|
|
211
|
+
index(s) == 0
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
# Check if string ends with s
|
|
215
|
+
#
|
|
216
|
+
# @param [String] s
|
|
217
|
+
# @return [Boolean]
|
|
218
|
+
#
|
|
219
|
+
def ends_with?(s)
|
|
220
|
+
rindex(s) == size - s.size
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
# Clean up path (replaces '..', '.' etc.)
|
|
224
|
+
#
|
|
225
|
+
# @return [String] cleaned path
|
|
226
|
+
#
|
|
227
|
+
def cleanpath
|
|
228
|
+
names = []
|
|
229
|
+
split('/').each do |name|
|
|
230
|
+
case name
|
|
231
|
+
when '..'
|
|
232
|
+
names.pop
|
|
233
|
+
when '.'
|
|
234
|
+
when ''
|
|
235
|
+
else
|
|
236
|
+
names.push name
|
|
237
|
+
end
|
|
238
|
+
end
|
|
239
|
+
names.join('/')
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
# Concatenate path components with /
|
|
243
|
+
#
|
|
244
|
+
# Calls {#cleanpath} on result.
|
|
245
|
+
#
|
|
246
|
+
# @param [String] path component
|
|
247
|
+
# @return [String] this string concatenated with path
|
|
248
|
+
#
|
|
249
|
+
def /(path)
|
|
250
|
+
"#{self}/#{path}".cleanpath
|
|
251
|
+
end
|
|
252
|
+
end
|