nitro 0.1.2
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/AUTHORS +8 -0
- data/ChangeLog +1546 -0
- data/LICENCE +32 -0
- data/README +278 -0
- data/RELEASES +7 -0
- data/Rakefile +79 -0
- data/bin/cluster.rb +219 -0
- data/doc/architecture.txt +28 -0
- data/doc/bugs.txt +7 -0
- data/doc/css.txt +20 -0
- data/doc/ideas.txt +120 -0
- data/doc/pg.txt +47 -0
- data/doc/svn.txt +82 -0
- data/doc/todo.txt +30 -0
- data/etc/new-project.rb +18 -0
- data/examples/simple/README +15 -0
- data/examples/simple/app.rb +31 -0
- data/examples/simple/conf/apache.conf +100 -0
- data/examples/simple/conf/config.rb +89 -0
- data/examples/simple/conf/debug-config.rb +53 -0
- data/examples/simple/conf/live-config.rb +48 -0
- data/examples/simple/conf/overrides.rb +9 -0
- data/examples/simple/conf/requires.rb +51 -0
- data/examples/simple/ctl +32 -0
- data/examples/simple/env.rb +33 -0
- data/examples/simple/install.rb +12 -0
- data/examples/simple/lib/articles/entities.rb +35 -0
- data/examples/simple/lib/articles/lc-en.rb +36 -0
- data/examples/simple/lib/articles/methods.rb +55 -0
- data/examples/simple/lib/articles/part.rb +58 -0
- data/examples/simple/logs/access_log +2 -0
- data/examples/simple/logs/apache.log +3 -0
- data/examples/simple/logs/app.log +1 -0
- data/examples/simple/logs/events.log +1 -0
- data/examples/simple/root/add-article.sx +15 -0
- data/examples/simple/root/article-form.ss +20 -0
- data/examples/simple/root/comments-form.ss +16 -0
- data/examples/simple/root/comments.si +30 -0
- data/examples/simple/root/index.sx +44 -0
- data/examples/simple/root/shader/shader.xsl +100 -0
- data/examples/simple/root/shader/style.css +9 -0
- data/examples/simple/root/view-article.sx +30 -0
- data/examples/tiny/app.rb +30 -0
- data/examples/tiny/conf/apache.conf +100 -0
- data/examples/tiny/conf/config.rb +67 -0
- data/examples/tiny/conf/requires.rb +40 -0
- data/examples/tiny/ctl +31 -0
- data/examples/tiny/logs/access_log +9 -0
- data/examples/tiny/logs/apache.log +9 -0
- data/examples/tiny/root/index.sx +35 -0
- data/lib/n/app/cluster.rb +219 -0
- data/lib/n/app/cookie.rb +86 -0
- data/lib/n/app/filters/autologin.rb +50 -0
- data/lib/n/app/fragment.rb +67 -0
- data/lib/n/app/handlers.rb +120 -0
- data/lib/n/app/handlers/code-handler.rb +184 -0
- data/lib/n/app/handlers/page-handler.rb +612 -0
- data/lib/n/app/request-part.rb +59 -0
- data/lib/n/app/request.rb +653 -0
- data/lib/n/app/script.rb +398 -0
- data/lib/n/app/server.rb +53 -0
- data/lib/n/app/session.rb +224 -0
- data/lib/n/app/user.rb +47 -0
- data/lib/n/app/webrick-servlet.rb +213 -0
- data/lib/n/app/webrick.rb +70 -0
- data/lib/n/application.rb +187 -0
- data/lib/n/config.rb +31 -0
- data/lib/n/db.rb +217 -0
- data/lib/n/db/README +232 -0
- data/lib/n/db/connection.rb +369 -0
- data/lib/n/db/make-release.sh +26 -0
- data/lib/n/db/managed.rb +235 -0
- data/lib/n/db/mixins.rb +282 -0
- data/lib/n/db/mysql.rb +342 -0
- data/lib/n/db/psql.rb +378 -0
- data/lib/n/db/tools.rb +110 -0
- data/lib/n/db/utils.rb +99 -0
- data/lib/n/events.rb +118 -0
- data/lib/n/l10n.rb +22 -0
- data/lib/n/logger.rb +33 -0
- data/lib/n/macros.rb +53 -0
- data/lib/n/mixins.rb +46 -0
- data/lib/n/parts.rb +154 -0
- data/lib/n/properties.rb +194 -0
- data/lib/n/server.rb +61 -0
- data/lib/n/server/PLAYBACK.txt +8 -0
- data/lib/n/server/RESEARCH.txt +13 -0
- data/lib/n/server/filter.rb +77 -0
- data/lib/n/shaders.rb +167 -0
- data/lib/n/sitemap.rb +188 -0
- data/lib/n/std.rb +69 -0
- data/lib/n/sync/clc.rb +108 -0
- data/lib/n/sync/handler.rb +221 -0
- data/lib/n/sync/server.rb +170 -0
- data/lib/n/tools/README +11 -0
- data/lib/n/ui/date-select.rb +74 -0
- data/lib/n/ui/pager.rb +187 -0
- data/lib/n/ui/popup.rb +45 -0
- data/lib/n/ui/select.rb +41 -0
- data/lib/n/ui/tabs.rb +34 -0
- data/lib/n/utils/array.rb +92 -0
- data/lib/n/utils/cache.rb +144 -0
- data/lib/n/utils/gfx.rb +108 -0
- data/lib/n/utils/hash.rb +148 -0
- data/lib/n/utils/html.rb +147 -0
- data/lib/n/utils/http.rb +98 -0
- data/lib/n/utils/mail.rb +28 -0
- data/lib/n/utils/number.rb +31 -0
- data/lib/n/utils/pool.rb +66 -0
- data/lib/n/utils/string.rb +297 -0
- data/lib/n/utils/template.rb +38 -0
- data/lib/n/utils/time.rb +91 -0
- data/lib/n/utils/uri.rb +193 -0
- data/lib/xsl/base.xsl +205 -0
- data/lib/xsl/ce.xsl +30 -0
- data/lib/xsl/localization.xsl +23 -0
- data/lib/xsl/xforms.xsl +26 -0
- data/test/run.rb +95 -0
- metadata +187 -0
data/lib/n/parts.rb
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
# = Part
|
|
2
|
+
#
|
|
3
|
+
# A part encapsulates a service in the Framework.
|
|
4
|
+
#
|
|
5
|
+
# code:
|
|
6
|
+
# George Moschovitis <gm@navel.gr>
|
|
7
|
+
#
|
|
8
|
+
# (c) 2004 Navel, all rights reserved.
|
|
9
|
+
# $Id: parts.rb 71 2004-10-18 10:50:22Z gmosx $
|
|
10
|
+
|
|
11
|
+
require "singleton"
|
|
12
|
+
|
|
13
|
+
require "n/l10n"
|
|
14
|
+
|
|
15
|
+
# gmosx, FIXME: safe is probably NOT needed!
|
|
16
|
+
$parts = N::SafeHash.new
|
|
17
|
+
$rewrites = N::SafeHash.new
|
|
18
|
+
$roles = N::SafeArray.new
|
|
19
|
+
|
|
20
|
+
module Kernel
|
|
21
|
+
|
|
22
|
+
# Special require for part definitions.
|
|
23
|
+
#
|
|
24
|
+
def require_part(path)
|
|
25
|
+
require("p/#{path}/part")
|
|
26
|
+
for lc in $lc_use
|
|
27
|
+
begin
|
|
28
|
+
require("p/#{path}/#{lc}")
|
|
29
|
+
rescue LoadError => e
|
|
30
|
+
# Drink it!
|
|
31
|
+
# $log.debug "No localization files for part '#{path}'" if $DBG
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Register a part
|
|
37
|
+
#
|
|
38
|
+
def register_part(part_class)
|
|
39
|
+
part = part_class.instance()
|
|
40
|
+
$parts[part.name] = part
|
|
41
|
+
|
|
42
|
+
# gmosx: DOESNT work yet, we have to manage
|
|
43
|
+
# the require order, perhaps DI/Copland can help
|
|
44
|
+
# here?
|
|
45
|
+
# auto require the parts this one depends
|
|
46
|
+
# on.
|
|
47
|
+
#
|
|
48
|
+
#for dep_part in part.dependencies
|
|
49
|
+
# require_part(dep_part)
|
|
50
|
+
#end
|
|
51
|
+
|
|
52
|
+
part.start()
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# ----------------------------------------------------------------------
|
|
58
|
+
|
|
59
|
+
module N
|
|
60
|
+
|
|
61
|
+
# = Part
|
|
62
|
+
#
|
|
63
|
+
# A module of functionality in the framework
|
|
64
|
+
#
|
|
65
|
+
# = Design:
|
|
66
|
+
#
|
|
67
|
+
# Q: Part should include Entity to allow optional runtime
|
|
68
|
+
# administration/customization ??
|
|
69
|
+
#
|
|
70
|
+
class Part
|
|
71
|
+
include Singleton
|
|
72
|
+
|
|
73
|
+
# the (internal) name of this part
|
|
74
|
+
attr_accessor :name
|
|
75
|
+
# the tile of this part (intended for humans)
|
|
76
|
+
attr_accessor :title
|
|
77
|
+
# a description of the part
|
|
78
|
+
attr_accessor :body
|
|
79
|
+
# the version of this part
|
|
80
|
+
attr_accessor :version
|
|
81
|
+
# the vendor
|
|
82
|
+
attr_accessor :vendor
|
|
83
|
+
# url of the vendor's homepage.
|
|
84
|
+
attr_accessor :vendor_url
|
|
85
|
+
# an array of part names this part depends on. The named
|
|
86
|
+
# parts are automatically required when requiring this part.
|
|
87
|
+
attr_accessor :dependencies
|
|
88
|
+
|
|
89
|
+
prop_accessor String, :path
|
|
90
|
+
|
|
91
|
+
def initialize
|
|
92
|
+
sitemap()
|
|
93
|
+
rewrites()
|
|
94
|
+
roles()
|
|
95
|
+
prepared_statements()
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# Install this part to the application. Called
|
|
99
|
+
# by the application installation scripts. Typically
|
|
100
|
+
# inserts bootstrap data in the database
|
|
101
|
+
#
|
|
102
|
+
def install
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# Uninstall this part. Typically cleans up the
|
|
106
|
+
# database.
|
|
107
|
+
#
|
|
108
|
+
def unistall
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
#
|
|
112
|
+
#
|
|
113
|
+
def start
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
#
|
|
117
|
+
#
|
|
118
|
+
def stop
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# Define the pages this part introduces.
|
|
122
|
+
#
|
|
123
|
+
def sitemap
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
# Define the rewrite rules for this part.
|
|
127
|
+
#
|
|
128
|
+
def rewrites
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# Define the roles this part introduces.
|
|
132
|
+
#
|
|
133
|
+
def roles
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# Define usefull prepared statements for this
|
|
137
|
+
# part
|
|
138
|
+
#
|
|
139
|
+
def prepared_statements
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
#
|
|
143
|
+
#
|
|
144
|
+
def to_s
|
|
145
|
+
@name
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
end # module
|
|
150
|
+
|
|
151
|
+
# Namespace for parts.
|
|
152
|
+
#
|
|
153
|
+
module P
|
|
154
|
+
end # module
|
data/lib/n/properties.rb
ADDED
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
# = Properties support
|
|
2
|
+
#
|
|
3
|
+
# Ruby attributes are typeless and generally this is good. Some times
|
|
4
|
+
# we need extra metadata though, for example in relational mapping, or
|
|
5
|
+
# web form population.
|
|
6
|
+
#
|
|
7
|
+
# Only Fixnums, Strings, Floats, Times, Booleans are converted.
|
|
8
|
+
#
|
|
9
|
+
# The default = methods do not force the types. A special
|
|
10
|
+
# __force_set method should be used instead.
|
|
11
|
+
#
|
|
12
|
+
# TODO:
|
|
13
|
+
# Perhaps a sync is needed in evals (!!!!)
|
|
14
|
+
#
|
|
15
|
+
# code: gmosx, drak, ekarak
|
|
16
|
+
#
|
|
17
|
+
# (c) 2004 Navel, all rights reserved.
|
|
18
|
+
# $Id: properties.rb 71 2004-10-18 10:50:22Z gmosx $
|
|
19
|
+
|
|
20
|
+
require "n/utils/array"
|
|
21
|
+
require "n/utils/hash"
|
|
22
|
+
|
|
23
|
+
module N
|
|
24
|
+
|
|
25
|
+
# Property Metadata
|
|
26
|
+
#
|
|
27
|
+
# === Design:
|
|
28
|
+
# - add default value in the extra sql
|
|
29
|
+
#
|
|
30
|
+
class Property
|
|
31
|
+
# the symbol of the property
|
|
32
|
+
attr_accessor :symbol
|
|
33
|
+
# the string representation of the symbol
|
|
34
|
+
attr_accessor :name
|
|
35
|
+
# the class of the property
|
|
36
|
+
attr_accessor :klass
|
|
37
|
+
# extra sql options for the property. if set,
|
|
38
|
+
# it must contain the sql_type for this class, the
|
|
39
|
+
# default is overriden.
|
|
40
|
+
attr_accessor :sql
|
|
41
|
+
|
|
42
|
+
def initialize(symbol, klass, sql = nil)
|
|
43
|
+
@symbol, @klass = symbol, klass
|
|
44
|
+
@sql = sql
|
|
45
|
+
@name = @symbol.to_s()
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def ==(other)
|
|
49
|
+
return @symbol == other.symbol
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
end # module
|
|
54
|
+
|
|
55
|
+
class Module
|
|
56
|
+
|
|
57
|
+
# Properties
|
|
58
|
+
# An array is used to enforce order.
|
|
59
|
+
attr_accessor :__props
|
|
60
|
+
|
|
61
|
+
# Metadata
|
|
62
|
+
# A hash of module metadata.
|
|
63
|
+
attr_accessor :__meta
|
|
64
|
+
|
|
65
|
+
# Define a property (== typed attribute)
|
|
66
|
+
# based on draks property redesign!
|
|
67
|
+
#
|
|
68
|
+
# ex:
|
|
69
|
+
# prop_accessor String, :title, :body
|
|
70
|
+
# prop_accessor String, "char(10) NOT NULL", :title, :body
|
|
71
|
+
#
|
|
72
|
+
def prop_accessor(*params)
|
|
73
|
+
klass = params.shift
|
|
74
|
+
|
|
75
|
+
# if the next param is a String, it is
|
|
76
|
+
# extra sql metadata.
|
|
77
|
+
if params.first.is_a?(String)
|
|
78
|
+
sql = params.shift
|
|
79
|
+
else
|
|
80
|
+
sql = nil
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# the rest of params are symbols
|
|
84
|
+
|
|
85
|
+
@__props = N::SafeArray.new() unless @__props
|
|
86
|
+
|
|
87
|
+
params.each { |s|
|
|
88
|
+
add_prop(N::Property.new(s, klass, sql))
|
|
89
|
+
}
|
|
90
|
+
end
|
|
91
|
+
# to be compatible with ruby parlance
|
|
92
|
+
alias_method :prop, :prop_accessor
|
|
93
|
+
|
|
94
|
+
# Add the property
|
|
95
|
+
# Optimize this
|
|
96
|
+
#
|
|
97
|
+
def add_prop(prop)
|
|
98
|
+
if idx = @__props.index(prop)
|
|
99
|
+
# override in case of duplicates. Keep the order of the props.
|
|
100
|
+
@__props[idx] = prop
|
|
101
|
+
else
|
|
102
|
+
@__props << prop
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# Precompile the property read/write methods
|
|
106
|
+
|
|
107
|
+
s, klass = prop.symbol, prop.klass
|
|
108
|
+
|
|
109
|
+
module_eval %{
|
|
110
|
+
def #{s}
|
|
111
|
+
return @#{s}
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def #{s}=(val)
|
|
115
|
+
@#{s} = val
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def __force_#{s}(val)
|
|
119
|
+
@#{s} = } + case klass.name
|
|
120
|
+
when Fixnum.name
|
|
121
|
+
"val.to_i()"
|
|
122
|
+
when String.name
|
|
123
|
+
"val.to_s()"
|
|
124
|
+
when Float.name
|
|
125
|
+
"val.to_f()"
|
|
126
|
+
when Time.name
|
|
127
|
+
"Time.parse(val.to_s())"
|
|
128
|
+
when TrueClass.name, FalseClass.name
|
|
129
|
+
"val.to_i() > 0"
|
|
130
|
+
else
|
|
131
|
+
"val"
|
|
132
|
+
end + %{
|
|
133
|
+
end
|
|
134
|
+
}
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
# Attach metadata
|
|
138
|
+
#
|
|
139
|
+
def meta(key, val)
|
|
140
|
+
@__meta = N::SafeHash.new unless @__meta
|
|
141
|
+
add_meta(key, val)
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
# Add the metadata
|
|
145
|
+
# TODO: Optimize this
|
|
146
|
+
#
|
|
147
|
+
def add_meta(key, val)
|
|
148
|
+
@__meta[key] = [] unless @__meta[key]
|
|
149
|
+
|
|
150
|
+
# guard against duplicates, no need to keep order.
|
|
151
|
+
@__meta[key].delete_if { |v| val == v }
|
|
152
|
+
@__meta[key] << val
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# This method is typically called before including other
|
|
156
|
+
# modules to preserve properties order.
|
|
157
|
+
#
|
|
158
|
+
def inherit_meta(mod = superclass)
|
|
159
|
+
# concat props.
|
|
160
|
+
if mod.__props
|
|
161
|
+
@__props = N::SafeArray.new unless @__props
|
|
162
|
+
|
|
163
|
+
mod.__props.each { |p|
|
|
164
|
+
add_prop(p)
|
|
165
|
+
}
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
# concat metadata
|
|
169
|
+
if mod.__meta
|
|
170
|
+
mod.__meta.each { |k, val|
|
|
171
|
+
val.each { |v|
|
|
172
|
+
meta(k, v)
|
|
173
|
+
} if val
|
|
174
|
+
}
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
# Override the default include method to also include the metadata.
|
|
179
|
+
# WARNING: can cause infinite loop if overriden again!
|
|
180
|
+
#
|
|
181
|
+
alias_method :old_include, :include
|
|
182
|
+
|
|
183
|
+
# A new version of include that includes the properties too.
|
|
184
|
+
#
|
|
185
|
+
def include(*modules)
|
|
186
|
+
old_include(*modules)
|
|
187
|
+
|
|
188
|
+
for m in modules
|
|
189
|
+
inherit_meta(m)
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
end
|
|
194
|
+
|
data/lib/n/server.rb
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# = Server
|
|
2
|
+
#
|
|
3
|
+
# Generic server infrastructure
|
|
4
|
+
#
|
|
5
|
+
# code:: gmosx
|
|
6
|
+
#
|
|
7
|
+
# (c) 2004 Navel, all rights reserved.
|
|
8
|
+
# $Id: server.rb 71 2004-10-18 10:50:22Z gmosx $
|
|
9
|
+
|
|
10
|
+
require "n/application"
|
|
11
|
+
|
|
12
|
+
module N
|
|
13
|
+
|
|
14
|
+
# = Server
|
|
15
|
+
#
|
|
16
|
+
# Base server class
|
|
17
|
+
#
|
|
18
|
+
class Server < N::Application
|
|
19
|
+
# the listening address/port for this server.
|
|
20
|
+
attr_reader :address, :port
|
|
21
|
+
|
|
22
|
+
def initialize(name = "Server")
|
|
23
|
+
super
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Start the server
|
|
27
|
+
#
|
|
28
|
+
def start
|
|
29
|
+
super
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Stop the server
|
|
33
|
+
#
|
|
34
|
+
def stop
|
|
35
|
+
super
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# The main server loop
|
|
39
|
+
#
|
|
40
|
+
def run
|
|
41
|
+
begin
|
|
42
|
+
while :RUNNING == @status
|
|
43
|
+
if live = IO.select(@ios, nil, nil, 2.0)
|
|
44
|
+
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
rescue
|
|
48
|
+
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Override this method in your custom server. This method is very
|
|
53
|
+
# flexible, you can spawn threads, use keep alive connections,
|
|
54
|
+
# handle+close, use handler pools, anything.
|
|
55
|
+
#
|
|
56
|
+
def handle
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
end # module
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
overload the servers accept method to store socket data in RECORD mode
|
|
4
|
+
and read sockets from a previously recorded stream in PLAYBACK mode.
|
|
5
|
+
|
|
6
|
+
timing information must be stored along the socket data
|
|
7
|
+
|
|
8
|
+
the centralized accept methot makes the scheme possible.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
|
|
2
|
+
patterns: ObjectPool, ProducerConsumer
|
|
3
|
+
|
|
4
|
+
produced consumer pattern: listener produces sockets, thread pool consumes
|
|
5
|
+
sockets from a queue.
|
|
6
|
+
|
|
7
|
+
--
|
|
8
|
+
|
|
9
|
+
gmosx: daemons does not belong here ??
|
|
10
|
+
gmosx: do not use a thread per connection, use a pool of conections that service
|
|
11
|
+
a fifo list of connections (sockets).
|
|
12
|
+
|
|
13
|
+
gmosx: use a pipeline request handling architecture
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# = Server filter
|
|
2
|
+
#
|
|
3
|
+
# code:
|
|
4
|
+
# George Moschovitis <gm@navel.gr>
|
|
5
|
+
#
|
|
6
|
+
# (c) 20024 Navel, all rights reserved.
|
|
7
|
+
# $Id: filter.rb 71 2004-10-18 10:50:22Z gmosx $
|
|
8
|
+
|
|
9
|
+
require "socket"
|
|
10
|
+
require "thread"
|
|
11
|
+
require "sync"
|
|
12
|
+
|
|
13
|
+
module N
|
|
14
|
+
|
|
15
|
+
# == Filter
|
|
16
|
+
#
|
|
17
|
+
# A server serves client requests by feeding the request/request pair
|
|
18
|
+
# to a pipeline of processing filters. This is not a simple linear pipeline.
|
|
19
|
+
# Instead it is what we call a 'folding' (hierarchical) pipeline: each
|
|
20
|
+
# filter encapsulates the next. In effect, the pipeline is a generalized
|
|
21
|
+
# filter!
|
|
22
|
+
#
|
|
23
|
+
# === Design:
|
|
24
|
+
#
|
|
25
|
+
# Filters are NOT singleton classes. This way we can assign one filter
|
|
26
|
+
# class to multiple resources, and keep statistics and metrics for
|
|
27
|
+
# each resource.
|
|
28
|
+
# A filter may contain state (attributes) for example metrics.
|
|
29
|
+
#
|
|
30
|
+
class ServerFilter
|
|
31
|
+
# the next filter in the pipeline.
|
|
32
|
+
attr_reader :next_filter
|
|
33
|
+
|
|
34
|
+
# set the filters next.
|
|
35
|
+
#
|
|
36
|
+
# example:
|
|
37
|
+
# LogFilter.new(TimeFilter.new(PageFilter.new))
|
|
38
|
+
|
|
39
|
+
def initialize(next_filter = nil)
|
|
40
|
+
@next_filter = next_filter
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# set the filters next (next_filter).
|
|
44
|
+
#
|
|
45
|
+
# example:
|
|
46
|
+
# LogFilter.new << TimeFilter.new << PageFilter.new
|
|
47
|
+
|
|
48
|
+
def << (next_filter = nil)
|
|
49
|
+
@next_filter = next_filter
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# override this method to implement your filter.
|
|
53
|
+
|
|
54
|
+
def process(request)
|
|
55
|
+
# preprocessing comes here...
|
|
56
|
+
|
|
57
|
+
# walk the pipeline
|
|
58
|
+
return process_next(request)
|
|
59
|
+
|
|
60
|
+
# postprocessing comes here...
|
|
61
|
+
|
|
62
|
+
# return the result...
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# process the next filter in the pipeline
|
|
66
|
+
|
|
67
|
+
def process_next(request)
|
|
68
|
+
if @next_filter
|
|
69
|
+
return @next_filter.process(request)
|
|
70
|
+
else
|
|
71
|
+
return nil
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
end # module
|