ruwiki 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Readme.rubygems +86 -0
- data/Readme.tarfile +65 -0
- data/bin/ruwiki +58 -0
- data/bin/ruwiki.cgi +87 -0
- data/bin/ruwiki_convert +56 -0
- data/bin/ruwiki_service.rb +82 -0
- data/bin/ruwiki_servlet +53 -0
- data/contrib/enscript-token.rb +55 -0
- data/contrib/rublog_integrator.rb +68 -0
- data/data/Default/ProjectIndex.ruwiki +49 -0
- data/data/Ruwiki/Antispam.ruwiki +65 -0
- data/data/Ruwiki/BugTracking.ruwiki +33 -0
- data/data/Ruwiki/ChangeLog.ruwiki +102 -0
- data/data/Ruwiki/Configuring_Ruwiki.ruwiki +151 -0
- data/data/Ruwiki/Extending_Ruwiki.ruwiki +317 -0
- data/data/Ruwiki/LicenseAndAuthorInfo.ruwiki +30 -0
- data/data/Ruwiki/ProjectIndex.ruwiki +84 -0
- data/data/Ruwiki/Roadmap.ruwiki +225 -0
- data/data/Ruwiki/RuwikiTemplatingLibrary.ruwiki +156 -0
- data/data/Ruwiki/RuwikiUtilities.ruwiki +157 -0
- data/data/Ruwiki/SandBox.ruwiki +9 -0
- data/data/Ruwiki/To_Do.ruwiki +51 -0
- data/data/Ruwiki/TroubleShooting.ruwiki +33 -0
- data/data/Ruwiki/WikiFeatures.ruwiki +17 -0
- data/data/Ruwiki/WikiMarkup.ruwiki +261 -0
- data/data/Tutorial/AddingPages.ruwiki +16 -0
- data/data/Tutorial/AddingProjects.ruwiki +16 -0
- data/data/Tutorial/ProjectIndex.ruwiki +11 -0
- data/data/Tutorial/SandBox.ruwiki +9 -0
- data/data/agents.banned +60 -0
- data/data/agents.readonly +321 -0
- data/data/hostip.banned +30 -0
- data/data/hostip.readonly +28 -0
- data/lib/ruwiki.rb +622 -0
- data/lib/ruwiki/auth.rb +56 -0
- data/lib/ruwiki/auth/gforge.rb +73 -0
- data/lib/ruwiki/backend.rb +318 -0
- data/lib/ruwiki/backend/flatfiles.rb +217 -0
- data/lib/ruwiki/config.rb +244 -0
- data/lib/ruwiki/exportable.rb +192 -0
- data/lib/ruwiki/handler.rb +342 -0
- data/lib/ruwiki/lang/de.rb +339 -0
- data/lib/ruwiki/lang/en.rb +334 -0
- data/lib/ruwiki/lang/es.rb +339 -0
- data/lib/ruwiki/page.rb +262 -0
- data/lib/ruwiki/servlet.rb +38 -0
- data/lib/ruwiki/template.rb +553 -0
- data/lib/ruwiki/utils.rb +24 -0
- data/lib/ruwiki/utils/command.rb +102 -0
- data/lib/ruwiki/utils/converter.rb +297 -0
- data/lib/ruwiki/utils/manager.rb +639 -0
- data/lib/ruwiki/utils/servletrunner.rb +295 -0
- data/lib/ruwiki/wiki.rb +147 -0
- data/lib/ruwiki/wiki/tokens.rb +136 -0
- data/lib/ruwiki/wiki/tokens/00default.rb +211 -0
- data/lib/ruwiki/wiki/tokens/01wikilinks.rb +166 -0
- data/lib/ruwiki/wiki/tokens/02actions.rb +63 -0
- data/lib/ruwiki/wiki/tokens/abbreviations.rb +40 -0
- data/lib/ruwiki/wiki/tokens/calendar.rb +147 -0
- data/lib/ruwiki/wiki/tokens/headings.rb +43 -0
- data/lib/ruwiki/wiki/tokens/lists.rb +112 -0
- data/lib/ruwiki/wiki/tokens/rubylists.rb +48 -0
- data/ruwiki.conf +22 -0
- data/ruwiki.pkg +0 -0
- data/templates/default/body.tmpl +19 -0
- data/templates/default/content.tmpl +7 -0
- data/templates/default/controls.tmpl +23 -0
- data/templates/default/edit.tmpl +27 -0
- data/templates/default/error.tmpl +14 -0
- data/templates/default/footer.tmpl +23 -0
- data/templates/default/ruwiki.css +297 -0
- data/templates/default/save.tmpl +8 -0
- data/templates/sidebar/body.tmpl +19 -0
- data/templates/sidebar/content.tmpl +8 -0
- data/templates/sidebar/controls.tmpl +8 -0
- data/templates/sidebar/edit.tmpl +27 -0
- data/templates/sidebar/error.tmpl +13 -0
- data/templates/sidebar/footer.tmpl +22 -0
- data/templates/sidebar/ruwiki.css +347 -0
- data/templates/sidebar/save.tmpl +10 -0
- data/templates/simple/body.tmpl +13 -0
- data/templates/simple/content.tmpl +7 -0
- data/templates/simple/controls.tmpl +8 -0
- data/templates/simple/edit.tmpl +25 -0
- data/templates/simple/error.tmpl +10 -0
- data/templates/simple/footer.tmpl +10 -0
- data/templates/simple/ruwiki.css +192 -0
- data/templates/simple/save.tmpl +8 -0
- data/tests/harness.rb +52 -0
- data/tests/tc_backend_flatfile.rb +103 -0
- data/tests/tc_bugs.rb +74 -0
- data/tests/tc_exportable.rb +64 -0
- data/tests/tc_template.rb +145 -0
- data/tests/tc_tokens.rb +335 -0
- data/tests/testall.rb +20 -0
- metadata +182 -0
@@ -0,0 +1,244 @@
|
|
1
|
+
#--
|
2
|
+
# Ruwiki
|
3
|
+
# Copyright � 2002 - 2004, Digikata and HaloStatue
|
4
|
+
# Alan Chen (alan@digikata.com)
|
5
|
+
# Austin Ziegler (ruwiki@halostatue.ca)
|
6
|
+
#
|
7
|
+
# Licensed under the same terms as Ruby.
|
8
|
+
#
|
9
|
+
# $Id: config.rb,v 1.17 2004/11/28 05:27:32 austin Exp $
|
10
|
+
#++
|
11
|
+
require 'ruwiki/exportable'
|
12
|
+
|
13
|
+
# Ruwiki configuration.
|
14
|
+
class Ruwiki::Config
|
15
|
+
include Ruwiki::Exportable
|
16
|
+
|
17
|
+
CONFIG_NAME = 'ruwiki.conf'
|
18
|
+
|
19
|
+
exportable_group 'ruwiki-config'
|
20
|
+
# Sets or returns the logger. The logger, if set, must respond to the same
|
21
|
+
# methods as WEBrick::Logger.
|
22
|
+
attr_accessor :logger
|
23
|
+
# Sets or returns the time format whenever time is outputted in Ruwiki.
|
24
|
+
# Default is <tt>%H:%M:%S</tt> (23:59:59).
|
25
|
+
attr_accessor :time_format
|
26
|
+
exportable :time_format
|
27
|
+
# Sets or returns the date format whenever time is outputted in Ruwiki.
|
28
|
+
# Default is <tt>%Y.%m.%d</tt> (2004.08.04).
|
29
|
+
attr_accessor :date_format
|
30
|
+
exportable :date_format
|
31
|
+
# Sets or returns the date-time format whenever time is outputted in
|
32
|
+
# Ruwiki. Default is <tt>%Y.%m.%d %H:%M:%S</tt> (2004.08.04 23:59:59).
|
33
|
+
attr_accessor :datetime_format
|
34
|
+
exportable :datetime_format
|
35
|
+
# Adds additional information to the (rare) error reports. Defaults to
|
36
|
+
# +false+.
|
37
|
+
attr_accessor :debug
|
38
|
+
exportable :debug
|
39
|
+
# The default page for display when Ruwiki is called without any arguments.
|
40
|
+
# Defaults to +ProjectIndex+
|
41
|
+
attr_accessor :default_page
|
42
|
+
exportable :default_page
|
43
|
+
# The default project for display when Ruwiki is called without any
|
44
|
+
# arguments or a project specification. Defaults to +Default+
|
45
|
+
attr_accessor :default_project
|
46
|
+
exportable :default_project
|
47
|
+
# The authentication mechanism name as a String. Corresponds to
|
48
|
+
# a filename that will be found in ruwiki/auth. The authenticator must
|
49
|
+
# have a single class method, +authenticate+, which accepts the
|
50
|
+
# +request+, the +response+, and the +#auth_options+. This API is
|
51
|
+
# a draft API and is likely to change in future versions of Ruwiki. In
|
52
|
+
# this version of Ruwiki, only one authentication mechanism will be
|
53
|
+
# found -- for dealing with authenticating users already logged into
|
54
|
+
# RubyForge.
|
55
|
+
attr_accessor :auth_mechanism
|
56
|
+
exportable :auth_mechanism
|
57
|
+
# Options for the authentication mechanism as a Hash. This will be
|
58
|
+
# passed to the authenticator defined in +#auth_mechanism+.
|
59
|
+
attr_accessor :auth_options
|
60
|
+
exportable :auth_options
|
61
|
+
# The storage type as a String. Corresponds to a filename that will be
|
62
|
+
# found in ruwiki/backend. NOTE: The yaml and marshal storage types have
|
63
|
+
# been removed from Ruwiki 0.9.0, to be replaced with a single storage
|
64
|
+
# type of Flatfiles. Now, the YAML and Marshal formats can be enabled by
|
65
|
+
# setting options in the @storage_options field.
|
66
|
+
attr_accessor :storage_type
|
67
|
+
exportable :storage_type
|
68
|
+
# The options for the specified storage type. This is a hash of hashes with
|
69
|
+
# auto-vifification. See the storage type for available options.
|
70
|
+
attr_reader :storage_options
|
71
|
+
exportable :storage_options
|
72
|
+
# The path for templates. Defaults to <tt>./templates/</tt>.
|
73
|
+
attr_accessor :template_path
|
74
|
+
exportable :template_path
|
75
|
+
# The name of the Wiki. Defaults to <tt>ruwiki</tt>
|
76
|
+
attr_accessor :title
|
77
|
+
exportable :title
|
78
|
+
# The email address of the webmaster for the Wiki. Defaults to +nil+.
|
79
|
+
attr_accessor :webmaster
|
80
|
+
exportable :webmaster
|
81
|
+
# The name of the Ruwiki CSS file. Defaults to <tt>ruwiki.css</tt>.
|
82
|
+
attr_accessor :css
|
83
|
+
exportable :css
|
84
|
+
# The template set. Templates are always named as
|
85
|
+
# <template_path>/<template_set>/<template_name>. Template filename. Must
|
86
|
+
# be reachable by File#read.
|
87
|
+
attr_accessor :template_set
|
88
|
+
exportable :template_set
|
89
|
+
# Ruwiki is internationalized. This method sets the Ruwiki error
|
90
|
+
# messages (and a few other messages) to the specified language Module.
|
91
|
+
# The language Module must have a constant Hash called +Message+
|
92
|
+
# containing a set of symbols and localized versions of the messages
|
93
|
+
# associated with them.
|
94
|
+
#
|
95
|
+
# If the file 'ruwiki/lang/es.rb' contains the module
|
96
|
+
# <tt>Ruwiki::Lang::ES</tt>, the error messages for RSS could be
|
97
|
+
# localized to Espa�ol thus:
|
98
|
+
#
|
99
|
+
# require 'ruwiki/lang/es'
|
100
|
+
# ...
|
101
|
+
# wiki.config.language = Ruwiki::Lang::ES
|
102
|
+
#
|
103
|
+
# Localization is per wiki instance. In a servlet environment, this may
|
104
|
+
# mean that only a single language is recognised.
|
105
|
+
#
|
106
|
+
# See Ruwiki::Lang::EN for more information.
|
107
|
+
attr_accessor :language
|
108
|
+
exportable :language
|
109
|
+
# The message hash.
|
110
|
+
attr_reader :message
|
111
|
+
|
112
|
+
def language=(ll) #:nodoc:
|
113
|
+
if ll.kind_of?(String)
|
114
|
+
@language = Ruwiki::Lang::const_get(ll.upcase)
|
115
|
+
else
|
116
|
+
@language = ll
|
117
|
+
end
|
118
|
+
@message = @language::Message
|
119
|
+
end
|
120
|
+
|
121
|
+
# Returns the specified template as a string.
|
122
|
+
def template(name)
|
123
|
+
File.read(File.join(@template_path, @template_set, "#{name.to_s}.tmpl"))
|
124
|
+
rescue Errno::ENOENT
|
125
|
+
raise ConfigError, message[:no_template_found] % [name.inspect, @template_set]
|
126
|
+
end
|
127
|
+
|
128
|
+
# Returns the CSS stylesheet content for the Wiki. This previously
|
129
|
+
# returned the <link> to the stylesheet, but instead returns a <style>
|
130
|
+
# block in the head so that the CSS is kept with the template set, which
|
131
|
+
# may be kept outside of the HTML area.
|
132
|
+
def css_link
|
133
|
+
%Q[<style type="text/css" media="screen,print">#{File.read(File.join(@template_path, @template_set, @css))}</style>]
|
134
|
+
end
|
135
|
+
|
136
|
+
# Creates a new configuration object.
|
137
|
+
def initialize(exportable = {})
|
138
|
+
rc = exportable['ruwiki-config'] || {}
|
139
|
+
@debug = (rc['debug'] == "false") ? false : true
|
140
|
+
@default_project = rc['default-project'] || "Default"
|
141
|
+
@default_page = rc['default-page'] || "ProjectIndex"
|
142
|
+
@auth_mechanism = rc['auth-mechanism'] || nil
|
143
|
+
|
144
|
+
case rc['auth-options']
|
145
|
+
when nil, ""
|
146
|
+
@auth_options = {}
|
147
|
+
else
|
148
|
+
@auth_options = Ruwiki::Exportable.load(rc['auth-options'])['default']
|
149
|
+
end
|
150
|
+
|
151
|
+
case rc['storage-type']
|
152
|
+
when nil, ""
|
153
|
+
@storage_type = 'flatfiles'
|
154
|
+
else
|
155
|
+
@storage_type = rc['storage-type']
|
156
|
+
end
|
157
|
+
|
158
|
+
# in 'type!name:<Tab>value\n' format.
|
159
|
+
if rc['storage-options'].nil? or rc['storage-options'].empty?
|
160
|
+
@storage_options = Hash.new { |hh, kk| hh[kk] = {} }
|
161
|
+
else
|
162
|
+
@storage_options = Ruwiki::Exportable.load(rc['storage-options'])
|
163
|
+
@storage_options.keys.each do |key|
|
164
|
+
@storage_options[key] = @storage_options.delete(key)
|
165
|
+
end
|
166
|
+
end
|
167
|
+
if @storage_options.empty?
|
168
|
+
@storage_options[@storage_type]['extension'] = "ruwiki"
|
169
|
+
@storage_options[@storage_type]['data-path'] = "./data"
|
170
|
+
@storage_options[@storage_type]['format'] = "exportable"
|
171
|
+
end
|
172
|
+
|
173
|
+
@storage_options.each_value do |vv|
|
174
|
+
if vv['extension'].nil? or vv['extension'].empty?
|
175
|
+
vv['extension'] = "ruwiki"
|
176
|
+
end
|
177
|
+
if vv['data-path'].nil? or vv['data-path'].empty?
|
178
|
+
vv['data-path'] = "./data"
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
@template_path = rc['template-path'] || "./templates/"
|
183
|
+
@template_set = rc['template-set'] || "default"
|
184
|
+
@css = rc['css'] || "ruwiki.css"
|
185
|
+
@webmaster = rc['webmaster']
|
186
|
+
@title = rc['title'] || "Ruwiki"
|
187
|
+
@time_format = rc['time-format'] || "%H:%M:%S"
|
188
|
+
@date_format = rc['date-format'] || "%Y.%m.%d"
|
189
|
+
@datetime_format = rc['datetime-format'] || "#{@date_format} #{@time_format}"
|
190
|
+
case rc['language']
|
191
|
+
when nil, ""
|
192
|
+
self.language = Ruwiki::Lang::EN
|
193
|
+
else
|
194
|
+
self.language = Ruwiki::Lang::const_get(rc['language'].upcase)
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
# Verifies that required configuration options are actually set. Right
|
199
|
+
# now, it only checks the values that are defaulted to +nil+.
|
200
|
+
def verify
|
201
|
+
raise ConfigError, message[:no_webmaster_defined] if @webmaster.nil? or @webmaster.empty?
|
202
|
+
raise ConfigError, message[:invalid_template_dir] % [@template_path] unless File.exists?(@template_path) and File.directory?(@template_path)
|
203
|
+
tt = File.join(@template_path, @template_set)
|
204
|
+
raise ConfigError, message[:no_template_set] % [@template_set] unless File.exists?(tt) and File.directory?(tt)
|
205
|
+
end
|
206
|
+
|
207
|
+
# Provides the canonical export hash.
|
208
|
+
def export
|
209
|
+
exportable = super
|
210
|
+
|
211
|
+
rc = exportable['ruwiki-config']
|
212
|
+
|
213
|
+
rc['auth-options'] = Ruwiki::Exportable.dump({ 'default' => rc['auth-options']})
|
214
|
+
|
215
|
+
rc['storage-options'] = Ruwiki::Exportable.dump(rc['storage-options'])
|
216
|
+
rc['storage-type'] = rc['storage-type'].to_s
|
217
|
+
rc['language'] = "#{rc['language']}".sub(/^.*?::([A-Z]+)$/, '\1').downcase
|
218
|
+
exportable
|
219
|
+
end
|
220
|
+
|
221
|
+
class << self
|
222
|
+
def write(file, config)
|
223
|
+
if file.respond_to?(:write)
|
224
|
+
file.puts(config.dump)
|
225
|
+
else
|
226
|
+
File.open(file, 'wb') { |ff| ff.puts(config.dump) }
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
def read(file)
|
231
|
+
data = nil
|
232
|
+
if file.respond_to?(:read)
|
233
|
+
data = file.read
|
234
|
+
else
|
235
|
+
File.open(file, 'rb') { |ff| data = ff.read }
|
236
|
+
end
|
237
|
+
hash = Ruwiki::Exportable.load(data)
|
238
|
+
|
239
|
+
Ruwiki::Config.new(hash)
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
class ConfigError < StandardError; end
|
244
|
+
end
|
@@ -0,0 +1,192 @@
|
|
1
|
+
#--
|
2
|
+
# Ruwiki
|
3
|
+
# Copyright � 2002 - 2004, Digikata and HaloStatue
|
4
|
+
# Alan Chen (alan@digikata.com)
|
5
|
+
# Austin Ziegler (ruwiki@halostatue.ca)
|
6
|
+
#
|
7
|
+
# Licensed under the same terms as Ruby.
|
8
|
+
#
|
9
|
+
# $Id: exportable.rb,v 1.3 2004/09/21 02:44:22 austin Exp $
|
10
|
+
#++
|
11
|
+
|
12
|
+
# == Synopsis
|
13
|
+
# Generalises a marshaling format that is easily read and edited by humans
|
14
|
+
# and is relatively easy to manage by software. When an attribute is marked
|
15
|
+
# #exportable, the name of the attribute is transformed and stored in
|
16
|
+
# a two-level hash, e.g.:
|
17
|
+
#
|
18
|
+
# exportable_group 'group1'
|
19
|
+
# exportable :var_1
|
20
|
+
# exportable :var_2
|
21
|
+
# exportable_group 'group2'
|
22
|
+
# exportable :var_3
|
23
|
+
# exportable :var_4
|
24
|
+
#
|
25
|
+
# Results in an exportable hash of:
|
26
|
+
#
|
27
|
+
# { 'group1' =>
|
28
|
+
# { 'var-1' => @var1,
|
29
|
+
# 'var-2' => @var2, },
|
30
|
+
# 'group2' =>
|
31
|
+
# { 'var-3' => @var3,
|
32
|
+
# 'var-4' => @var4, }, }
|
33
|
+
#
|
34
|
+
module Ruwiki::Exportable
|
35
|
+
class InvalidFormatError < RuntimeError; end
|
36
|
+
|
37
|
+
class << self
|
38
|
+
# Adds two methods and an attribute to the class that is including Exportable
|
39
|
+
#
|
40
|
+
# <tt>__exportables</tt>:: Contains the list of exportable symbols by group.
|
41
|
+
# <tt>exportable_group</tt>:: Defines the current group for exportable
|
42
|
+
# symbols. Default is 'default'.
|
43
|
+
# <tt>exportable</tt>:: Accepts two arguments, the attribute being
|
44
|
+
# exported and an option hash, containing the
|
45
|
+
# values :name and :group, where :name
|
46
|
+
# indicates the name of the attribute (so
|
47
|
+
# that the default name transformation is
|
48
|
+
# not applied) and :group overrides the
|
49
|
+
# current #exportable_group. By default, the
|
50
|
+
# name of the attribute is transformed such
|
51
|
+
# that underscores are converted to dashes
|
52
|
+
# (<tt>var_1</tt> becomes 'var-1').
|
53
|
+
def append_features(mod)
|
54
|
+
super
|
55
|
+
|
56
|
+
class << mod
|
57
|
+
attr_reader :__exportables
|
58
|
+
|
59
|
+
define_method(:exportable_group) do |name|
|
60
|
+
@__exportable_group = name || 'default'
|
61
|
+
end
|
62
|
+
|
63
|
+
define_method(:exportable) do |*symset|
|
64
|
+
symbol = symset.shift
|
65
|
+
options = symset.shift || {}
|
66
|
+
|
67
|
+
@__exportables ||= {}
|
68
|
+
|
69
|
+
options[:name] ||= symbol.to_s.gsub(/_/, '-')
|
70
|
+
options[:group] ||= @__exportable_group || 'default'
|
71
|
+
|
72
|
+
@__exportables[options[:group]] ||= {}
|
73
|
+
@__exportables[options[:group]][options[:name]] = "@#{symbol.to_s}".intern
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# Looks for comments. Comments may ONLY be on single lines.
|
79
|
+
COMMENT_RE = %r{^#}
|
80
|
+
# Looks for newlines
|
81
|
+
NL_RE = %r{\n}
|
82
|
+
# Looks for a line that indicates an exportable value. See #dump.
|
83
|
+
HEADER_RE = %r{^([a-z][-a-z]+)!([a-z][-a-z]+):[ \t](.*)$}
|
84
|
+
# Looks for an indented group indicating that the last group is
|
85
|
+
# a multiline value.
|
86
|
+
FIRST_TAB = %r{^[ \t]}
|
87
|
+
|
88
|
+
# Dumps the provided exportable hash in the form:
|
89
|
+
#
|
90
|
+
# section!name:<Tab>Value
|
91
|
+
# section!name:<Space>Value
|
92
|
+
#
|
93
|
+
# Multiline values are indented either one space or one tab:
|
94
|
+
#
|
95
|
+
# section!name:<Tab>Value Line 1
|
96
|
+
# <Tab>Value Line 2
|
97
|
+
# <Tab>Value Line 3
|
98
|
+
# <Tab>Value Line 4
|
99
|
+
#
|
100
|
+
# All values in the exportable hash are converted to string
|
101
|
+
# representations, so only values that can meaningfully be reinstantiated
|
102
|
+
# from string representations should be stored in the exportable hash. It
|
103
|
+
# is the responsibility of the class preparing the exportable hash
|
104
|
+
# through Exportable#export to make the necessary transformations.
|
105
|
+
def dump(export_hash)
|
106
|
+
dumpstr = ""
|
107
|
+
|
108
|
+
export_hash.keys.sort.each do |sect|
|
109
|
+
export_hash[sect].keys.sort.each do |item|
|
110
|
+
val = export_hash[sect][item].to_s.split(NL_RE).join("\n\t")
|
111
|
+
dumpstr << "#{sect}!#{item}:\t#{val}\n"
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
dumpstr
|
116
|
+
end
|
117
|
+
|
118
|
+
# Loads a buffer in the form provided by #dump into an exportable hash.
|
119
|
+
# Skips comment lines.
|
120
|
+
def load(buffer)
|
121
|
+
hash = {}
|
122
|
+
return hash if buffer.nil? or buffer.empty?
|
123
|
+
|
124
|
+
# Split the buffer and eliminate comments.
|
125
|
+
buffer = buffer.split(NL_RE).delete_if { |line| line =~ COMMENT_RE }
|
126
|
+
|
127
|
+
if HEADER_RE.match(buffer[0]).nil?
|
128
|
+
raise Ruwiki::Exportable::InvalidFormatError
|
129
|
+
end
|
130
|
+
|
131
|
+
sect = item = nil
|
132
|
+
|
133
|
+
buffer.each do |line|
|
134
|
+
line.chomp!
|
135
|
+
match = HEADER_RE.match(line)
|
136
|
+
|
137
|
+
# If there is no match, add the current line to the previous match.
|
138
|
+
# Remove the leading \t, though.
|
139
|
+
if match.nil?
|
140
|
+
raise Ruwiki::Exportable::InvalidFormatError if FIRST_TAB.match(line).nil?
|
141
|
+
hash[sect][item] << "\n#{line.gsub(FIRST_TAB, '')}"
|
142
|
+
else
|
143
|
+
sect = match.captures[0]
|
144
|
+
item = match.captures[1]
|
145
|
+
hash[sect] ||= {}
|
146
|
+
hash[sect][item] = match.captures[2]
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
hash
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
# Converts #exportable attributes to an exportable hash, in the form:
|
155
|
+
# { 'group1' =>
|
156
|
+
# { 'var-1' => @var1,
|
157
|
+
# 'var-2' => @var2, },
|
158
|
+
# 'group2' =>
|
159
|
+
# { 'var-3' => @var3,
|
160
|
+
# 'var-4' => @var4, }, }
|
161
|
+
#
|
162
|
+
# Classes that #include Exportable are encouraged to override export to
|
163
|
+
# ensure safe transformations of values. An example use might be:
|
164
|
+
#
|
165
|
+
# class TimeClass
|
166
|
+
# include Ruwiki::Exportable
|
167
|
+
#
|
168
|
+
# def export
|
169
|
+
# sym = super
|
170
|
+
#
|
171
|
+
# sym['default']['time'] = sym['default']['time'].to_i
|
172
|
+
# sym
|
173
|
+
# end
|
174
|
+
#
|
175
|
+
# In this way, the 'time' value is converted to an integer rather than the
|
176
|
+
# default string representation.
|
177
|
+
def export
|
178
|
+
sym = {}
|
179
|
+
|
180
|
+
self.class.__exportables.each do |group, gval|
|
181
|
+
gname = group || @__exportable_group || 'default'
|
182
|
+
gsym = {}
|
183
|
+
gval.each do |name, nval|
|
184
|
+
val = self.instance_variable_get(nval)
|
185
|
+
gsym[name] = val unless val.nil?
|
186
|
+
end
|
187
|
+
sym[gname] = gsym
|
188
|
+
end
|
189
|
+
|
190
|
+
sym
|
191
|
+
end
|
192
|
+
end
|
@@ -0,0 +1,342 @@
|
|
1
|
+
#--
|
2
|
+
# Ruwiki
|
3
|
+
# Copyright � 2002 - 2004, Digikata and HaloStatue
|
4
|
+
# Alan Chen (alan@digikata.com)
|
5
|
+
# Austin Ziegler (ruwiki@halostatue.ca)
|
6
|
+
#
|
7
|
+
# Licensed under the same terms as Ruby.
|
8
|
+
#
|
9
|
+
# $Id: handler.rb,v 1.8 2004/11/28 05:27:32 austin Exp $
|
10
|
+
#++
|
11
|
+
class Ruwiki::Handler
|
12
|
+
class << self
|
13
|
+
# Generate a new Handler pair from a CGI request.
|
14
|
+
def from_cgi(cgi, output_stream = $stdout)
|
15
|
+
Ruwiki::Handler.new do |o|
|
16
|
+
o.request = Ruwiki::Handler::CGIRequest.new(cgi)
|
17
|
+
o.response = Ruwiki::Handler::CGIResponse.new(cgi, output_stream)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# Generate a new Handler pair from a WEBrick request.
|
22
|
+
def from_webrick(req, res)
|
23
|
+
Ruwiki::Handler.new do |o|
|
24
|
+
o.request = Ruwiki::Handler::WEBrickRequest.new(req)
|
25
|
+
o.response = Ruwiki::Handler::WEBrickResponse.new(res)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# Returns the handler's request object.
|
31
|
+
attr_accessor :request
|
32
|
+
# Returns the handler's response object.
|
33
|
+
attr_accessor :response
|
34
|
+
|
35
|
+
# Creates the handler pair.
|
36
|
+
def initialize(&block) #:yields: self
|
37
|
+
@request = nil
|
38
|
+
@response = nil
|
39
|
+
yield self if block_given?
|
40
|
+
end
|
41
|
+
|
42
|
+
# Essentially a clone of WEBrick::Cookie for use with Ruwiki.
|
43
|
+
class Cookie
|
44
|
+
attr_reader :name
|
45
|
+
attr_accessor :value
|
46
|
+
attr_accessor :version
|
47
|
+
|
48
|
+
FIELDS = %w(domain path secure comment max_age expires)
|
49
|
+
|
50
|
+
FIELDS.each { |field| attr_accessor field.intern }
|
51
|
+
|
52
|
+
def initialize(name, value)
|
53
|
+
@name = name
|
54
|
+
@value = value
|
55
|
+
@version = 0 # Netscape Cookie
|
56
|
+
|
57
|
+
FIELDS.each { |field| instance_variable_set("@#{field}", nil) }
|
58
|
+
|
59
|
+
yield self if block_given?
|
60
|
+
end
|
61
|
+
|
62
|
+
def expires=(t) #:nodoc:
|
63
|
+
@expires = if t.nil? or t.kind_of?(Time)
|
64
|
+
t
|
65
|
+
else
|
66
|
+
Time.parse(t.to_s)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def to_s
|
71
|
+
ret = "#{@name}=#{@value}"
|
72
|
+
ret << "; Version=#{@version.to_s}" if @version > 0
|
73
|
+
ret << "; Domain=#{@domain}" if @domain
|
74
|
+
ret << "; Expires=#{CGI::rfc1123_date(@expires)}" if @expires
|
75
|
+
ret << "; Max-Age=#{CGI::rfc1123_date(@max_age)}" if @max_age
|
76
|
+
ret << "; Comment=#{@comment}" if @comment
|
77
|
+
ret << "; Path=#{@path}" if @path
|
78
|
+
ret << "; Secure" if @secure
|
79
|
+
ret
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# Represents an abstract incoming request. This insulates the rest of
|
84
|
+
# the code from knowing whether parameters are passed as part of the
|
85
|
+
# path, as parameters in the URL, or in some other fashion.
|
86
|
+
class AbstractRequest
|
87
|
+
def initialize(*args)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
# Handles all requests from web applications.
|
92
|
+
#
|
93
|
+
# Subclasses should provide:
|
94
|
+
# @parameters:: Hash-like object that responds to #[] and #hash_key?]
|
95
|
+
# @environment:: Hash-like object that responds to #[]
|
96
|
+
class AbstractWebRequest < AbstractRequest
|
97
|
+
# The parameters provided via the web request.
|
98
|
+
attr_reader :parameters
|
99
|
+
# The environment provided to the web request.
|
100
|
+
attr_reader :environment
|
101
|
+
# The request path.
|
102
|
+
attr_reader :path
|
103
|
+
|
104
|
+
# The list of cookies.
|
105
|
+
attr_reader :cookies
|
106
|
+
|
107
|
+
def each_parameter #:yields parameter, value:
|
108
|
+
@parameters.each { |kk, vv| yield kk, vv }
|
109
|
+
end
|
110
|
+
|
111
|
+
def each_environment #:yields variable, value
|
112
|
+
@environment.each { |kk, vv| yield kk, vv }
|
113
|
+
end
|
114
|
+
|
115
|
+
def each_cookie #:yields name, value:
|
116
|
+
@cookies.each { |kk, vv| yield kk, vv }
|
117
|
+
end
|
118
|
+
|
119
|
+
# Return the URL of our server.
|
120
|
+
def server_url
|
121
|
+
res = "http://" # should detect whether we're in secure server mode.
|
122
|
+
if @environment['HTTP_HOST']
|
123
|
+
res << @environment['HTTP_HOST']
|
124
|
+
else
|
125
|
+
res << "#{@environment['SERVER_NAME']}:#{@environment['SERVER_PORT']}"
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
# Return the URL of this script.
|
130
|
+
def script_url
|
131
|
+
server_url << @environment['SCRIPT_NAME'].to_s
|
132
|
+
end
|
133
|
+
|
134
|
+
# Return the URL of this request.
|
135
|
+
def request_url
|
136
|
+
res = script_url
|
137
|
+
res << @environment['PATH_INFO'] if @environment['PATH_INFO']
|
138
|
+
query = @environment['QUERY_STRING']
|
139
|
+
res << "?#{@environment['QUERY_STRING']}" if query && !query.empty?
|
140
|
+
res
|
141
|
+
end
|
142
|
+
|
143
|
+
# Convert a file path into a URL
|
144
|
+
def make_url(project, path)
|
145
|
+
"#{server_url}/#{project}/#{path}"
|
146
|
+
end
|
147
|
+
|
148
|
+
def determine_request_path
|
149
|
+
@path = ""
|
150
|
+
return @path if @environment['PATH_INFO'].nil?
|
151
|
+
@path = @environment['PATH_INFO'].dup
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
# Request for CGI-based activity to ruwiki.
|
156
|
+
class CGIRequest < AbstractWebRequest
|
157
|
+
def initialize(cgi, output_stream = $stdout)
|
158
|
+
@environment = ENV
|
159
|
+
@cgi = cgi
|
160
|
+
@parameters = {}
|
161
|
+
cgi.params.each { |kk, vv| @parameters[kk] = vv[0] }
|
162
|
+
@cookies = {}
|
163
|
+
cgi.cookies.each do |name, cookie|
|
164
|
+
@cookies[name] = Ruwiki::Handler::Cookie.new(name, cookie.value) do |oc|
|
165
|
+
oc.version = cookie.version if cookie.respond_to?(:version)
|
166
|
+
oc.domain = cookie.domain
|
167
|
+
oc.path = cookie.path
|
168
|
+
oc.secure = cookie.secure
|
169
|
+
oc.comment = cookie.comment if cookie.respond_to?(:comment)
|
170
|
+
oc.expires = cookie.expires
|
171
|
+
end
|
172
|
+
end
|
173
|
+
super
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
# Request for WEBrick based servlet activity to ruwiki.
|
178
|
+
class WEBrickRequest < AbstractWebRequest
|
179
|
+
def initialize(req)
|
180
|
+
@environment = req.meta_vars
|
181
|
+
@parameters = req.query
|
182
|
+
@cookies = {}
|
183
|
+
req.cookies.each do |rqc|
|
184
|
+
@cookies[rqc.name] = Ruwiki::Handler::Cookie.new(rqc.name, rqc.value) do |oc|
|
185
|
+
oc.version = rqc.version
|
186
|
+
oc.domain = rqc.domain
|
187
|
+
oc.path = rqc.path
|
188
|
+
oc.secure = rqc.secure
|
189
|
+
oc.comment = rqc.comment
|
190
|
+
oc.expires = rqc.expires
|
191
|
+
oc.max_age = rqc.max_age
|
192
|
+
end
|
193
|
+
end
|
194
|
+
super
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
# Used to write responses in different execution environments such as
|
199
|
+
# CGI and Webrick.
|
200
|
+
#
|
201
|
+
# If you want to create a new response object, you'll need to implement
|
202
|
+
# #add_header, #write_headers, #write_cookies, and #<<.
|
203
|
+
#
|
204
|
+
# The Response object is instantiated with an output stream which must
|
205
|
+
# supply +<<+ and +puts+ methods.
|
206
|
+
class AbstractResponse
|
207
|
+
# Add to the list of headers to be sent back to the client.
|
208
|
+
def add_header(key, value)
|
209
|
+
raise "Not implemented"
|
210
|
+
end
|
211
|
+
|
212
|
+
# Write the accumulated headers back to the client.
|
213
|
+
def write_headers
|
214
|
+
raise "Not implemented"
|
215
|
+
end
|
216
|
+
|
217
|
+
# Write the string to the client.
|
218
|
+
def <<(string)
|
219
|
+
raise "Not implemented"
|
220
|
+
end
|
221
|
+
|
222
|
+
def add_cookies(*cookies)
|
223
|
+
cookies.each do |cookie|
|
224
|
+
@cookies << cookie
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
def write_cookies
|
229
|
+
raise "Not implemented"
|
230
|
+
end
|
231
|
+
|
232
|
+
# output_stream must respond to #<< and #puts.
|
233
|
+
def initialize(output_stream = $stdout)
|
234
|
+
@headers = {}
|
235
|
+
@cookies = []
|
236
|
+
@written = false
|
237
|
+
@status = nil
|
238
|
+
@output_stream = output_stream
|
239
|
+
end
|
240
|
+
|
241
|
+
def written?
|
242
|
+
@written
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
# CGIResponse is the response object for CGI mode.
|
247
|
+
class CGIResponse < AbstractResponse
|
248
|
+
# output_stream must respond to #<< and #puts.
|
249
|
+
def initialize(cgi, output_stream = $stdout)
|
250
|
+
@cgi = cgi
|
251
|
+
@done = {
|
252
|
+
:headers => false,
|
253
|
+
:cookies => false,
|
254
|
+
:body => false
|
255
|
+
}
|
256
|
+
super(output_stream)
|
257
|
+
end
|
258
|
+
|
259
|
+
# Add the header pair for later output as a CGI header.
|
260
|
+
def add_header(key, value)
|
261
|
+
@headers[key] = value
|
262
|
+
end
|
263
|
+
|
264
|
+
# Write the headers to the stream. The headers can only be written
|
265
|
+
# once.
|
266
|
+
def write_headers
|
267
|
+
return if @done[:headers]
|
268
|
+
@headers.each { |key, value| @output_stream.puts "#{key}: #{value}\r\n" }
|
269
|
+
write_cookies
|
270
|
+
@output_stream.puts
|
271
|
+
@done[:headers] = true
|
272
|
+
end
|
273
|
+
|
274
|
+
# Write the cookies to the stream. The cookies can only be written
|
275
|
+
# once.
|
276
|
+
def write_cookies
|
277
|
+
return if @done[:cookies]
|
278
|
+
@cookies.each do |cookie|
|
279
|
+
@output_stream.puts "Set-Cookie: #{cookie.to_s}"
|
280
|
+
end
|
281
|
+
@done[:cookes] = true
|
282
|
+
end
|
283
|
+
|
284
|
+
# Output the string to the stream provided.
|
285
|
+
def <<(string)
|
286
|
+
@output_stream << string
|
287
|
+
@written = true
|
288
|
+
end
|
289
|
+
|
290
|
+
def write_status(status)
|
291
|
+
unless status.nil?
|
292
|
+
@output_stream << status
|
293
|
+
@written = true
|
294
|
+
end
|
295
|
+
end
|
296
|
+
end
|
297
|
+
|
298
|
+
# WEBrickResponse is the response object for WEBrick servlet mode.
|
299
|
+
class WEBrickResponse < AbstractResponse
|
300
|
+
def initialize(webrick_response)
|
301
|
+
@response = webrick_response
|
302
|
+
@cookies = []
|
303
|
+
@done = {
|
304
|
+
:headers => false,
|
305
|
+
:cookies => false,
|
306
|
+
:body => false
|
307
|
+
}
|
308
|
+
end
|
309
|
+
|
310
|
+
def add_header(key, value)
|
311
|
+
@response[key] = value
|
312
|
+
end
|
313
|
+
|
314
|
+
# Copy the cookies into the WEBrick::HTTPResponse cookies array.
|
315
|
+
def write_cookies
|
316
|
+
return if @done[:cookies]
|
317
|
+
@cookies.each do |cookie|
|
318
|
+
@response.cookies << cookie.to_s
|
319
|
+
end
|
320
|
+
@done[:cookes] = true
|
321
|
+
end
|
322
|
+
|
323
|
+
def write_headers
|
324
|
+
write_cookies
|
325
|
+
# Webrick will take care of this on its own.
|
326
|
+
end
|
327
|
+
|
328
|
+
def <<(string)
|
329
|
+
@response.body << string.to_s
|
330
|
+
@written = true
|
331
|
+
end
|
332
|
+
|
333
|
+
def write_status(status)
|
334
|
+
unless status.nil?
|
335
|
+
match = %r{^HTTP/(?:\d|\.)+ (\d+) .*}.match(status)
|
336
|
+
@response.status = match.captures[0]
|
337
|
+
@response.body << status
|
338
|
+
@written = true
|
339
|
+
end
|
340
|
+
end
|
341
|
+
end
|
342
|
+
end
|