nitro 0.3.0 → 0.4.1
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/ChangeLog +284 -0
- data/{LICENCE → LICENSE} +1 -1
- data/README +13 -17
- data/RELEASES +13 -1
- data/Rakefile +1 -9
- data/bin/cluster.rb +5 -5
- data/examples/blog/README +45 -0
- data/examples/blog/apache.conf +0 -0
- data/examples/blog/app.rb +21 -0
- data/examples/blog/config.rb +88 -0
- data/examples/blog/lib/blog.rb +104 -0
- data/examples/blog/log/app.log +117 -0
- data/examples/blog/root/comments.xhtml +38 -0
- data/examples/blog/root/entry_form.xhtml +18 -0
- data/examples/blog/root/index.xhtml +43 -0
- data/examples/blog/root/login.xhtml +24 -0
- data/examples/blog/root/m/bubbles.gif +0 -0
- data/examples/blog/root/m/comments_curve.gif +0 -0
- data/examples/blog/root/m/down.gif +0 -0
- data/examples/blog/root/m/footer_bg.gif +0 -0
- data/examples/blog/root/m/garrow.gif +0 -0
- data/examples/blog/root/m/gbull.gif +0 -0
- data/examples/blog/root/m/grbull.gif +0 -0
- data/examples/blog/root/m/h1_bg.gif +0 -0
- data/examples/blog/root/m/header_bg.gif +0 -0
- data/examples/blog/root/m/obull.gif +0 -0
- data/examples/blog/root/m/page_bg.gif +0 -0
- data/examples/blog/root/m/side_title_bg.gif +0 -0
- data/examples/blog/root/m/sidebar_bg.gif +0 -0
- data/examples/blog/root/recent_posts.xhtml +14 -0
- data/examples/blog/root/style.css +201 -0
- data/examples/blog/root/style.xsl +118 -0
- data/examples/blog/root/view_entry.xhtml +29 -0
- data/examples/og/run.rb +27 -29
- data/examples/tiny/README +3 -4
- data/examples/tiny/app.rb +6 -16
- data/examples/tiny/config.rb +30 -0
- data/examples/tiny/log/app.log +23 -0
- data/examples/tiny/root/{index.sx → index.xhtml} +3 -6
- data/lib/{n/std.rb → nitro.rb} +9 -4
- data/lib/{n → nitro}/application.rb +13 -7
- data/lib/{n → nitro}/config.rb +38 -8
- data/lib/{n → nitro}/events.rb +1 -1
- data/lib/{n → nitro}/l10n.rb +1 -1
- data/lib/{n → nitro}/logger.rb +8 -8
- data/lib/{n → nitro}/macros.rb +3 -3
- data/lib/{n → nitro}/mixins.rb +1 -1
- data/lib/nitro/properties.rb +301 -0
- data/lib/{n → nitro}/server.rb +2 -2
- data/lib/{n → nitro}/server/appserver.rb +14 -5
- data/lib/{n → nitro}/server/cluster.rb +5 -5
- data/lib/{n → nitro}/server/cookie.rb +1 -1
- data/lib/nitro/server/dispatcher.rb +66 -0
- data/lib/{n → nitro}/server/filters.rb +7 -7
- data/lib/{n → nitro}/server/filters/autologin.rb +3 -3
- data/lib/{n → nitro}/server/fragment.rb +3 -3
- data/lib/{n → nitro}/server/handlers.rb +3 -3
- data/lib/nitro/server/render.rb +200 -0
- data/lib/{n → nitro}/server/request.rb +6 -6
- data/lib/{n → nitro}/server/requestpart.rb +5 -5
- data/lib/{n → nitro}/server/script.rb +3 -3
- data/lib/{n → nitro}/server/server.rb +4 -4
- data/lib/{n → nitro}/server/session.rb +3 -3
- data/lib/nitro/server/shaders.rb +165 -0
- data/lib/{n → nitro}/server/user.rb +1 -1
- data/lib/nitro/server/webrick.rb +175 -0
- data/lib/nitro/service.rb +25 -0
- data/lib/{n → nitro}/sitemap.rb +2 -2
- data/lib/{n → nitro}/ui/date-select.rb +0 -0
- data/lib/{n → nitro}/ui/pager.rb +1 -1
- data/lib/{n → nitro}/ui/popup.rb +1 -1
- data/lib/{n → nitro}/ui/select.rb +1 -1
- data/lib/{n → nitro}/ui/tabs.rb +1 -1
- data/lib/{n → nitro}/utils/array.rb +1 -1
- data/lib/{n → nitro}/utils/cache.rb +1 -1
- data/lib/{n → nitro}/utils/gfx.rb +1 -1
- data/lib/{n → nitro}/utils/hash.rb +1 -1
- data/lib/{n → nitro}/utils/html.rb +1 -1
- data/lib/{n → nitro}/utils/http.rb +1 -1
- data/lib/{n → nitro}/utils/mail.rb +1 -1
- data/lib/{n → nitro}/utils/number.rb +1 -1
- data/lib/{n → nitro}/utils/pool.rb +1 -1
- data/lib/{n → nitro}/utils/string.rb +19 -95
- data/lib/{n → nitro}/utils/template.rb +0 -0
- data/lib/{n → nitro}/utils/time.rb +1 -1
- data/lib/{n → nitro}/utils/uri.rb +3 -3
- data/lib/nitro/version.rb +11 -0
- data/lib/{n/og.rb → og.rb} +61 -31
- data/lib/{n/og → og}/backend.rb +13 -7
- data/lib/{n/og → og}/backends/mysql.rb +43 -39
- data/lib/{n/og → og}/backends/psql.rb +42 -38
- data/lib/{n/og → og}/connection.rb +21 -9
- data/lib/{n/og → og}/meta.rb +18 -12
- data/lib/xsl/base.xsl +11 -88
- data/test/n/server/tc_cookie.rb +1 -1
- data/test/n/server/tc_filters.rb +1 -1
- data/test/n/server/tc_request.rb +3 -3
- data/test/n/server/tc_requestpart.rb +2 -2
- data/test/n/server/tc_session.rb +1 -1
- data/test/n/tc_events.rb +1 -1
- data/test/n/tc_og.rb +16 -18
- data/test/n/tc_properties.rb +22 -18
- data/test/n/tc_sitemap.rb +2 -2
- data/test/n/ui/tc_pager.rb +4 -4
- data/test/n/utils/tc_cache.rb +1 -1
- data/test/n/utils/tc_hash.rb +1 -1
- data/test/n/utils/tc_html.rb +1 -1
- data/test/n/utils/tc_http.rb +1 -1
- data/test/n/utils/tc_number.rb +1 -1
- data/test/n/utils/tc_strings.rb +1 -46
- data/test/n/utils/tc_uri.rb +1 -1
- metadata +101 -108
- data/examples/simple/README +0 -42
- data/examples/simple/app.rb +0 -31
- data/examples/simple/conf/apache.conf +0 -100
- data/examples/simple/conf/config.rb +0 -72
- data/examples/simple/conf/debug-config.rb +0 -26
- data/examples/simple/conf/live-config.rb +0 -26
- data/examples/simple/conf/requires.rb +0 -43
- data/examples/simple/ctl +0 -32
- data/examples/simple/env.rb +0 -32
- data/examples/simple/install.rb +0 -12
- data/examples/simple/lib/articles/entities.rb +0 -37
- data/examples/simple/lib/articles/lc-en.rb +0 -36
- data/examples/simple/lib/articles/methods.rb +0 -55
- data/examples/simple/lib/articles/part.rb +0 -57
- data/examples/simple/root/add-article.sx +0 -15
- data/examples/simple/root/article-form.ss +0 -20
- data/examples/simple/root/comments-form.ss +0 -16
- data/examples/simple/root/comments.si +0 -30
- data/examples/simple/root/index.sx +0 -44
- data/examples/simple/root/shader/shader.xsl +0 -100
- data/examples/simple/root/shader/style.css +0 -9
- data/examples/simple/root/view-article.sx +0 -29
- data/examples/tiny/conf/config.rb +0 -62
- data/examples/tiny/conf/requires.rb +0 -33
- data/examples/tiny/ctl +0 -16
- data/lib/n/parts.rb +0 -157
- data/lib/n/properties.rb +0 -199
- data/lib/n/server/dispatcher.rb +0 -55
- data/lib/n/server/handlers/code-handler.rb +0 -182
- data/lib/n/server/handlers/page-handler.rb +0 -612
- data/lib/n/server/webrick.rb +0 -283
- data/lib/n/shaders.rb +0 -166
- data/lib/n/sync/clc.rb +0 -110
- data/lib/n/sync/handler.rb +0 -229
- data/lib/n/sync/server.rb +0 -176
- data/lib/p/README +0 -1
data/lib/n/properties.rb
DELETED
@@ -1,199 +0,0 @@
|
|
1
|
-
# = Properties support
|
2
|
-
#
|
3
|
-
# code:
|
4
|
-
# * George Moschovitis <gm@navel.gr>
|
5
|
-
# design:
|
6
|
-
# * Anastastios Koutoumanos <ak@navel.gr>
|
7
|
-
# * Elias Karakoulakis <ekarak@ktismata.com>
|
8
|
-
#
|
9
|
-
# (c) 2004 Navel, all rights reserved.
|
10
|
-
# $Id: properties.rb 116 2004-10-29 16:26:32Z gmosx $
|
11
|
-
|
12
|
-
require "n/utils/array"
|
13
|
-
require "n/utils/hash"
|
14
|
-
|
15
|
-
module N
|
16
|
-
|
17
|
-
# Property Metadata
|
18
|
-
#
|
19
|
-
# Ruby attributes are typeless and generally this is good. Some times
|
20
|
-
# we need extra metadata though, for example in relational mapping, or
|
21
|
-
# web form population.
|
22
|
-
#
|
23
|
-
# Only Fixnums, Strings, Floats, Times, Booleans are converted.
|
24
|
-
#
|
25
|
-
# The default = methods do not force the types. A special
|
26
|
-
# __force_set method should be used instead.
|
27
|
-
#
|
28
|
-
# TODO:
|
29
|
-
# Perhaps a sync is needed in evals (!!!!)
|
30
|
-
#
|
31
|
-
# === Design:
|
32
|
-
#
|
33
|
-
# - add default value in the extra sql
|
34
|
-
#
|
35
|
-
class Property
|
36
|
-
# the symbol of the property
|
37
|
-
attr_accessor :symbol
|
38
|
-
# the string representation of the symbol
|
39
|
-
attr_accessor :name
|
40
|
-
# the class of the property
|
41
|
-
attr_accessor :klass
|
42
|
-
# extra sql options for the property. if set,
|
43
|
-
# it must contain the sql_type for this class, the
|
44
|
-
# default is overriden.
|
45
|
-
attr_accessor :sql
|
46
|
-
|
47
|
-
def initialize(symbol, klass, sql = nil)
|
48
|
-
@symbol, @klass = symbol, klass
|
49
|
-
@sql = sql
|
50
|
-
@name = @symbol.to_s()
|
51
|
-
end
|
52
|
-
|
53
|
-
def ==(other)
|
54
|
-
return @symbol == other.symbol
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
end # module
|
59
|
-
|
60
|
-
class Module
|
61
|
-
|
62
|
-
# Properties
|
63
|
-
# An array is used to enforce order.
|
64
|
-
attr_accessor :__props
|
65
|
-
|
66
|
-
# Metadata
|
67
|
-
# A hash of module metadata.
|
68
|
-
attr_accessor :__meta
|
69
|
-
|
70
|
-
# Define a property (== typed attribute)
|
71
|
-
# based on draks property redesign!
|
72
|
-
#
|
73
|
-
# ex:
|
74
|
-
# prop_accessor String, :title, :body
|
75
|
-
# prop_accessor String, "char(10) NOT NULL", :title, :body
|
76
|
-
#
|
77
|
-
def prop_accessor(*params)
|
78
|
-
klass = params.shift
|
79
|
-
|
80
|
-
# if the next param is a String, it is
|
81
|
-
# extra sql metadata.
|
82
|
-
if params.first.is_a?(String)
|
83
|
-
sql = params.shift
|
84
|
-
else
|
85
|
-
sql = nil
|
86
|
-
end
|
87
|
-
|
88
|
-
# the rest of params are symbols
|
89
|
-
|
90
|
-
@__props = N::SafeArray.new() unless @__props
|
91
|
-
|
92
|
-
params.each do |s|
|
93
|
-
add_prop(N::Property.new(s, klass, sql))
|
94
|
-
end
|
95
|
-
end
|
96
|
-
# to be compatible with ruby parlance
|
97
|
-
alias_method :prop, :prop_accessor
|
98
|
-
|
99
|
-
# Add the property
|
100
|
-
# Optimize this
|
101
|
-
#
|
102
|
-
def add_prop(prop)
|
103
|
-
if idx = @__props.index(prop)
|
104
|
-
# override in case of duplicates. Keep the order of the props.
|
105
|
-
@__props[idx] = prop
|
106
|
-
else
|
107
|
-
@__props << prop
|
108
|
-
end
|
109
|
-
|
110
|
-
# Precompile the property read/write methods
|
111
|
-
|
112
|
-
s, klass = prop.symbol, prop.klass
|
113
|
-
|
114
|
-
module_eval %{
|
115
|
-
def #{s}
|
116
|
-
return @#{s}
|
117
|
-
end
|
118
|
-
|
119
|
-
def #{s}=(val)
|
120
|
-
@#{s} = val
|
121
|
-
end
|
122
|
-
|
123
|
-
def __force_#{s}(val)
|
124
|
-
@#{s} = } + case klass.name
|
125
|
-
when Fixnum.name
|
126
|
-
"val.to_i()"
|
127
|
-
when String.name
|
128
|
-
"val.to_s()"
|
129
|
-
when Float.name
|
130
|
-
"val.to_f()"
|
131
|
-
when Time.name
|
132
|
-
"Time.parse(val.to_s())"
|
133
|
-
when TrueClass.name, FalseClass.name
|
134
|
-
"val.to_i() > 0"
|
135
|
-
else
|
136
|
-
"val"
|
137
|
-
end + %{
|
138
|
-
end
|
139
|
-
}
|
140
|
-
end
|
141
|
-
|
142
|
-
# Attach metadata
|
143
|
-
#
|
144
|
-
def meta(key, val)
|
145
|
-
@__meta = N::SafeHash.new unless @__meta
|
146
|
-
add_meta(key, val)
|
147
|
-
end
|
148
|
-
|
149
|
-
# Add the metadata
|
150
|
-
# TODO: Optimize this
|
151
|
-
#
|
152
|
-
def add_meta(key, val)
|
153
|
-
@__meta[key] = [] unless @__meta[key]
|
154
|
-
|
155
|
-
# guard against duplicates, no need to keep order.
|
156
|
-
@__meta[key].delete_if { |v| val == v }
|
157
|
-
@__meta[key] << val
|
158
|
-
end
|
159
|
-
|
160
|
-
# This method is typically called before including other
|
161
|
-
# modules to preserve properties order.
|
162
|
-
#
|
163
|
-
def inherit_meta(mod = superclass)
|
164
|
-
# concat props.
|
165
|
-
if mod.__props
|
166
|
-
@__props = N::SafeArray.new unless @__props
|
167
|
-
|
168
|
-
mod.__props.each { |p|
|
169
|
-
add_prop(p)
|
170
|
-
}
|
171
|
-
end
|
172
|
-
|
173
|
-
# concat metadata
|
174
|
-
if mod.__meta
|
175
|
-
mod.__meta.each { |k, val|
|
176
|
-
val.each { |v|
|
177
|
-
meta(k, v)
|
178
|
-
} if val
|
179
|
-
}
|
180
|
-
end
|
181
|
-
end
|
182
|
-
|
183
|
-
# Override the default include method to also include the metadata.
|
184
|
-
# WARNING: can cause infinite loop if overriden again!
|
185
|
-
#
|
186
|
-
alias_method :old_include, :include
|
187
|
-
|
188
|
-
# A new version of include that includes the properties too.
|
189
|
-
#
|
190
|
-
def include(*modules)
|
191
|
-
old_include(*modules)
|
192
|
-
|
193
|
-
for m in modules
|
194
|
-
inherit_meta(m)
|
195
|
-
end
|
196
|
-
end
|
197
|
-
|
198
|
-
end
|
199
|
-
|
data/lib/n/server/dispatcher.rb
DELETED
@@ -1,55 +0,0 @@
|
|
1
|
-
module N
|
2
|
-
|
3
|
-
#
|
4
|
-
#
|
5
|
-
class Dispatcher
|
6
|
-
|
7
|
-
def process(request)
|
8
|
-
service = ...
|
9
|
-
method = ...
|
10
|
-
query_string = ...
|
11
|
-
end
|
12
|
-
|
13
|
-
end
|
14
|
-
|
15
|
-
end # module
|
16
|
-
|
17
|
-
__END__
|
18
|
-
|
19
|
-
/login
|
20
|
-
/logout
|
21
|
-
|
22
|
-
/service/method?params
|
23
|
-
|
24
|
-
|
25
|
-
/fora/view_forum/1
|
26
|
-
|
27
|
-
/fora/view_forum?fid=1
|
28
|
-
|
29
|
-
|
30
|
-
/fora/view_forum.sx
|
31
|
-
|
32
|
-
if file exists evaluate in the controller a render method.
|
33
|
-
|
34
|
-
.xhtml
|
35
|
-
.xinc
|
36
|
-
|
37
|
-
class ForaController
|
38
|
-
|
39
|
-
def view_forum
|
40
|
-
@parent = Forum.db_get(request["pid"])
|
41
|
-
end
|
42
|
-
|
43
|
-
def render_view_forum
|
44
|
-
end
|
45
|
-
|
46
|
-
end
|
47
|
-
|
48
|
-
<?r for message in @parent.messages ?>
|
49
|
-
<li>#{message.title}</li>
|
50
|
-
<?r end ?>
|
51
|
-
|
52
|
-
|
53
|
-
one method, different views
|
54
|
-
html,
|
55
|
-
xml (for rpc)
|
@@ -1,182 +0,0 @@
|
|
1
|
-
# code:
|
2
|
-
# * George Moschovitis <gm@navel.gr>
|
3
|
-
#
|
4
|
-
# (c) 2004 Navel, all rights reserved.
|
5
|
-
# $Id: code-handler.rb 112 2004-10-27 10:59:55Z gmosx $
|
6
|
-
|
7
|
-
require "cgi"
|
8
|
-
require "singleton"
|
9
|
-
require "sync"
|
10
|
-
|
11
|
-
require "n/utils/cache"
|
12
|
-
require "n/utils/uri"
|
13
|
-
require "n/server/script"
|
14
|
-
require "n/server/fragment"
|
15
|
-
require "n/server/handlers"
|
16
|
-
|
17
|
-
module N
|
18
|
-
|
19
|
-
# = CodeHandler
|
20
|
-
#
|
21
|
-
# web server handler that executes ruby code (logic)
|
22
|
-
# Caching is NOT SUPPORTED (and not needed anyway)
|
23
|
-
#
|
24
|
-
# TODO:
|
25
|
-
#
|
26
|
-
# probably extend PageHandler from CodeHandler and not vice versa.
|
27
|
-
#
|
28
|
-
class CodeHandler < ScriptHandler
|
29
|
-
|
30
|
-
# process is called ONLY for top level scripts.
|
31
|
-
#
|
32
|
-
# no need for synchronization, the page-script is thread safe.
|
33
|
-
# if you use thread-unsafe code in your script you are responsible
|
34
|
-
# for synchronization.
|
35
|
-
#
|
36
|
-
# TODO:
|
37
|
-
# add timing code here
|
38
|
-
#
|
39
|
-
def process(request)
|
40
|
-
# gmosx, FIXME: temporarily here, move somewhere ELSE!
|
41
|
-
|
42
|
-
request.locale = $lc_map[request.user.locale] || $lc_en
|
43
|
-
script = get_compiled_script(request.path)
|
44
|
-
fragment = eval_script(script, request)
|
45
|
-
|
46
|
-
# walk the filter pipeline
|
47
|
-
super
|
48
|
-
|
49
|
-
return fragment, script
|
50
|
-
end
|
51
|
-
|
52
|
-
# evaluate the request script in the context of the current request.
|
53
|
-
# returns the output of the script, ie the fragment body.
|
54
|
-
#
|
55
|
-
# === Design:
|
56
|
-
#
|
57
|
-
# I think that the script caching logic should be here, because it nicelly
|
58
|
-
# encapsulates transform and compile.
|
59
|
-
#
|
60
|
-
def eval_script(script, request)
|
61
|
-
|
62
|
-
$log.debug "Evaluating, #{request.path}" if $DBG
|
63
|
-
|
64
|
-
fragment = Fragment.new(:key)
|
65
|
-
|
66
|
-
# try to render the script, report errors.
|
67
|
-
begin
|
68
|
-
fragment.body = script.__render(request)
|
69
|
-
rescue N::ScriptExitException => see
|
70
|
-
# the script raised a ScripteExitException to force a premature
|
71
|
-
# end. Surpress this error!
|
72
|
-
rescue => ex
|
73
|
-
log_error(request, ex)
|
74
|
-
end
|
75
|
-
|
76
|
-
return fragment
|
77
|
-
end
|
78
|
-
|
79
|
-
# === Output:
|
80
|
-
# defcode: the code that customizes the base script class.
|
81
|
-
# pagecode: the code that renders the fragment.
|
82
|
-
#
|
83
|
-
# === REMARKS:
|
84
|
-
# - this method is evaluated in compile time, so we cannot pass
|
85
|
-
# or use the request/request pair. gmosx: NO, we do pass request,
|
86
|
-
# some data can and SHOULD be used to prepare the transform.
|
87
|
-
#
|
88
|
-
def transform_script(path, hash, shader = nil)
|
89
|
-
|
90
|
-
path = overload_path(path)
|
91
|
-
|
92
|
-
# load the page text from the script
|
93
|
-
pagecode = ""
|
94
|
-
|
95
|
-
pagecode = File.read("#$root_dir/#{path}")
|
96
|
-
|
97
|
-
# convert to ruby code
|
98
|
-
pagecode.gsub!(/<<</, "__out << %{")
|
99
|
-
pagecode.gsub!(/>>>/, "}")
|
100
|
-
|
101
|
-
script = %{
|
102
|
-
class CodeScript_#{hash} < CodeScript
|
103
|
-
def initialize(path)
|
104
|
-
super
|
105
|
-
@pagecode = <<-'PAGECODE'
|
106
|
-
#{pagecode}
|
107
|
-
PAGECODE
|
108
|
-
@defcode = nil
|
109
|
-
end
|
110
|
-
def __render(request)
|
111
|
-
__out = ""
|
112
|
-
lc = request.locale
|
113
|
-
#{pagecode}
|
114
|
-
return __out
|
115
|
-
end
|
116
|
-
end
|
117
|
-
return CodeScript_#{hash}.new("#{path}")
|
118
|
-
}
|
119
|
-
|
120
|
-
return script
|
121
|
-
end
|
122
|
-
|
123
|
-
# try to get the script from the cache. invalidates the compiled version and return nil
|
124
|
-
# if the script is modified since compile time. Also takes active shader and localization
|
125
|
-
# into account. If script is not compiled, transform and compile it.
|
126
|
-
#
|
127
|
-
# Output:
|
128
|
-
# the compiled script. Throws exception if the script does not exist.
|
129
|
-
#
|
130
|
-
def get_compiled_script(path, hash = nil)
|
131
|
-
compiled_script = nil
|
132
|
-
key = calc_script_key(path, hash)
|
133
|
-
|
134
|
-
# gmosx: $reload_scripts is typically set to true when debugging
|
135
|
-
# statically included files (.ss)
|
136
|
-
unless $reload_scripts
|
137
|
-
|
138
|
-
compiled_script = @@compiled_script_cache[key]
|
139
|
-
|
140
|
-
# gmosx: monitor scripts should be explicit!
|
141
|
-
if $srv_monitor_scripts and compiled_script and (File.mtime(compiled_script.path) > compiled_script.__create_time)
|
142
|
-
$log.debug "Script '#{path}' externaly modified, recompiling" if $DBG
|
143
|
-
compiled_script = nil
|
144
|
-
end
|
145
|
-
|
146
|
-
end
|
147
|
-
|
148
|
-
unless compiled_script
|
149
|
-
# the script is not cached, so load, transform and compile it!
|
150
|
-
$log.debug "Compiling script '#{key}'" if $DBG
|
151
|
-
|
152
|
-
script = transform_script(path, key)
|
153
|
-
compiled_script = compile_script(script)
|
154
|
-
|
155
|
-
@@compiled_script_cache[key] = compiled_script
|
156
|
-
end
|
157
|
-
|
158
|
-
return compiled_script
|
159
|
-
end
|
160
|
-
|
161
|
-
def calc_script_key(path, hash)
|
162
|
-
return "#{path}#{hash}".gsub(/[^\w]/, "")
|
163
|
-
end
|
164
|
-
|
165
|
-
end # CodeHandler
|
166
|
-
|
167
|
-
# = Codescript
|
168
|
-
#
|
169
|
-
# encapsulates the script defining Ruby Code to execute.
|
170
|
-
# effectively acts as a Generator.
|
171
|
-
#
|
172
|
-
# === Requirements:
|
173
|
-
#
|
174
|
-
# - the generated class should be a singleton, no need to stress
|
175
|
-
# the garbage collector.
|
176
|
-
# - the render method should be thread safe.
|
177
|
-
#
|
178
|
-
class CodeScript < Script
|
179
|
-
|
180
|
-
end # CodeScript
|
181
|
-
|
182
|
-
end # module
|
@@ -1,612 +0,0 @@
|
|
1
|
-
# code:
|
2
|
-
# * George Moschovitis <gm@navel.gr>
|
3
|
-
# * Anastasios Koutoumanos <ak@navel.gr>
|
4
|
-
#
|
5
|
-
# (c) 2004 Navel, all rights reserved.
|
6
|
-
# $Id: page-handler.rb 112 2004-10-27 10:59:55Z gmosx $
|
7
|
-
|
8
|
-
require "cgi"
|
9
|
-
require "singleton"
|
10
|
-
require "sync"
|
11
|
-
|
12
|
-
require "n/utils/cache"
|
13
|
-
require "n/utils/uri"
|
14
|
-
require "n/server/script"
|
15
|
-
require "n/server/fragment"
|
16
|
-
require "n/server/handlers"
|
17
|
-
|
18
|
-
module N
|
19
|
-
|
20
|
-
# = PageHandler
|
21
|
-
#
|
22
|
-
# web server handler that render xml pages (.sx scripts).
|
23
|
-
# This is the main handler of the Nitro Application Server.
|
24
|
-
#
|
25
|
-
# The handler evaluates the given script. The result of
|
26
|
-
# this evaluation is called a Fragment. The result of a top
|
27
|
-
# level script, ie a top level fragments is called a page.
|
28
|
-
#
|
29
|
-
# == Advantages over the original .rx format:
|
30
|
-
#
|
31
|
-
# - xml based
|
32
|
-
# - strips <!-- comments
|
33
|
-
# - allows multiline comments
|
34
|
-
# - allows pre-transformation with xlst (free transformation)
|
35
|
-
# - allows validation of xhtml
|
36
|
-
# - allows syntax highlighting
|
37
|
-
#
|
38
|
-
# == TODO:
|
39
|
-
#
|
40
|
-
# - move shader selection in a script method, that can
|
41
|
-
# be overriden (user specific shaders).
|
42
|
-
# - compress the output some more, every byte
|
43
|
-
# counts (bandwidth == money)
|
44
|
-
# - use something like FormValidator to validate parameters
|
45
|
-
# for sub-fragments.
|
46
|
-
#
|
47
|
-
# === FIXME:
|
48
|
-
# - correct encoding of fragment hash
|
49
|
-
# (query string, shader, user etc)
|
50
|
-
#
|
51
|
-
# === Design:
|
52
|
-
#
|
53
|
-
# break the rendering process in sub methods to make testable
|
54
|
-
# and verifiable (and easier to read).
|
55
|
-
#
|
56
|
-
# Statically included scripts (.ss) SHOULD be valid xml files (even
|
57
|
-
# though they are not required) to be automatically verifiable and
|
58
|
-
# compatible with editors. The xml prologue (<?xml / <root>) is removed.
|
59
|
-
#
|
60
|
-
# Be carefull about statically included files scope issues. This is the
|
61
|
-
# reason why we do not use statically included files everywhere.
|
62
|
-
#
|
63
|
-
# === WARNING:
|
64
|
-
#
|
65
|
-
# no need to lock cache io, we use a safe cache!
|
66
|
-
#
|
67
|
-
class PageHandler < ScriptHandler
|
68
|
-
|
69
|
-
# process is called ONLY for top level scripts.
|
70
|
-
#
|
71
|
-
# no need for synchronization, the page-script is thread safe.
|
72
|
-
# if you use thread-unsafe code in your script you are responsible
|
73
|
-
# for synchronization.
|
74
|
-
#
|
75
|
-
# TODO:
|
76
|
-
# add timing code here
|
77
|
-
#
|
78
|
-
def process(request)
|
79
|
-
# gmosx, FIXME: temporarily here, move somewhere ELSE!
|
80
|
-
|
81
|
-
request.locale = $lc_map[request.user.locale] || $lc_en
|
82
|
-
request.shader = calc_shader(request)
|
83
|
-
request.tag = calc_tag(request)
|
84
|
-
request.top_script = script = get_compiled_script(request.path, request.tag, request.shader)
|
85
|
-
|
86
|
-
# Action requests should be uncacheable, to be safe.
|
87
|
-
# No need for a check here! all actions should use consume which
|
88
|
-
# sets the uncacheable flag.
|
89
|
-
#
|
90
|
-
# request.uncacheable = true if request.delete("*act*")
|
91
|
-
# request.uncacheable = true if request.query_string =~ /\$.*\$/
|
92
|
-
|
93
|
-
script.__init_render(request)
|
94
|
-
|
95
|
-
# dont cache admin pages, FIXME: use less checks here!!
|
96
|
-
|
97
|
-
if script.__cache?(request) and (not request.uncacheable) and
|
98
|
-
(not request.session["ADMIN_MODE"]) and (not $reload_scripts)
|
99
|
-
if etag = script.__etag(request)
|
100
|
-
request.headers["cache-control"] = "pubic, must-revalidate"
|
101
|
-
request.headers["etag"] = etag
|
102
|
-
end
|
103
|
-
else
|
104
|
-
# gmosx, FIXME: add the correct cache control header here!!
|
105
|
-
etag = nil
|
106
|
-
end
|
107
|
-
|
108
|
-
if etag && etag == request.headers["IF-NONE-MATCH"]
|
109
|
-
request.set_not_modified!
|
110
|
-
else
|
111
|
-
fragment = eval_script(script, request)
|
112
|
-
end
|
113
|
-
|
114
|
-
# walk the filter pipeline
|
115
|
-
super
|
116
|
-
|
117
|
-
return fragment, script
|
118
|
-
end
|
119
|
-
|
120
|
-
# Process sub level scripts.
|
121
|
-
#
|
122
|
-
def sub_process(request)
|
123
|
-
script = get_compiled_script(request.path, request.tag, request.shader)
|
124
|
-
script.__init_render(request)
|
125
|
-
fragment = eval_script(script, request)
|
126
|
-
|
127
|
-
return fragment, script
|
128
|
-
end
|
129
|
-
|
130
|
-
# evaluate the request script in the context of the current request.
|
131
|
-
# returns the output of the script, ie the fragment body.
|
132
|
-
#
|
133
|
-
# === Design:
|
134
|
-
#
|
135
|
-
# I think that the script caching logic should be here, because it nicelly
|
136
|
-
# encapsulates transform and compile.
|
137
|
-
#
|
138
|
-
def eval_script(script, request)
|
139
|
-
|
140
|
-
etag = script.__etag(request)
|
141
|
-
|
142
|
-
unless fragment = script.__cache_get(etag)
|
143
|
-
|
144
|
-
# no suitable fragment exists in the cache, so render the script.
|
145
|
-
$log.debug "Rendering, #{request.path}" if $DBG
|
146
|
-
|
147
|
-
fragment = Fragment.new
|
148
|
-
|
149
|
-
# try to render the script, report errors.
|
150
|
-
begin
|
151
|
-
fragment.body = script.__render(request)
|
152
|
-
|
153
|
-
if script.__cache?(request)
|
154
|
-
script.__cache_put(etag, fragment) unless request.uncacheable
|
155
|
-
end
|
156
|
-
rescue N::ScriptExitException => see
|
157
|
-
# the script raised a ScripteExitException to force a premature
|
158
|
-
# end. Surpress this error!
|
159
|
-
rescue => ex
|
160
|
-
log_error(request, ex)
|
161
|
-
end
|
162
|
-
|
163
|
-
end
|
164
|
-
|
165
|
-
return fragment
|
166
|
-
end
|
167
|
-
|
168
|
-
# === Output:
|
169
|
-
# defcode: the code that customizes the base script class.
|
170
|
-
# pagecode: the code that renders the fragment.
|
171
|
-
#
|
172
|
-
# === TODO:
|
173
|
-
# - we have to keep the original script code to apply multiple shaders
|
174
|
-
#
|
175
|
-
# === REMARKS:
|
176
|
-
# - this method is evaluated in compile time, so we cannot pass
|
177
|
-
# or use the request/request pair. gmosx: NO, we do pass request,
|
178
|
-
# some data can and SHOULD be used to prepare the transform.
|
179
|
-
#
|
180
|
-
def transform_script(path, hash, shader = nil)
|
181
|
-
|
182
|
-
path = overload_path(path)
|
183
|
-
|
184
|
-
initcode = ""
|
185
|
-
defcode = ""
|
186
|
-
sub_scripts = []
|
187
|
-
filename = "#$root_dir/#{path}"
|
188
|
-
cached_filename = ".cache/#{shader}#{path}.rb"
|
189
|
-
|
190
|
-
# load the page text from the script.
|
191
|
-
# check if a cached transformed script exists.
|
192
|
-
# also staticaly includes all <?include xxx ?> files.
|
193
|
-
# this also validates the xhtml (cool side-effect).
|
194
|
-
begin
|
195
|
-
document = ::File.read(filename)
|
196
|
-
|
197
|
-
# calculate mtime
|
198
|
-
mtime = ::File.stat(filename).mtime
|
199
|
-
document.scan(/<\?include xl:href="(.*?)"(.*)\?>/) { |match|
|
200
|
-
match = overload_path(match[0])
|
201
|
-
imtime = ::File.stat("#$root_dir/#{match}").mtime
|
202
|
-
mtime = imtime if imtime > mtime
|
203
|
-
}
|
204
|
-
# also check the shader mtime
|
205
|
-
# FIXME: even better should check mtime now!
|
206
|
-
mtime = shader.mtime if shader.mtime > mtime
|
207
|
-
|
208
|
-
# calculate subscripts
|
209
|
-
document.scan(/<x:include xl:href="(.*?)"(.*)>/) { |match|
|
210
|
-
match = overload_path(match[0])
|
211
|
-
sub_path = "#{match.split("?")[0]}"
|
212
|
-
sub_scripts << get_compiled_script(sub_path, hash, shader)
|
213
|
-
}
|
214
|
-
|
215
|
-
# check if a cached transformed script exists
|
216
|
-
# FIXME: encode shader and shader mtime.
|
217
|
-
if false # ::File.exists?(cached_filename) and ::File.stat(cached_filename).mtime > mtime
|
218
|
-
return ::File.read(cached_filename), sub_scripts
|
219
|
-
end
|
220
|
-
|
221
|
-
$log.debug "Transforming '#{path}'" if $DBG
|
222
|
-
|
223
|
-
# static includes
|
224
|
-
# the target file is included at compile time.
|
225
|
-
#
|
226
|
-
# gmosx: must be xformed before the <?r pi.
|
227
|
-
# ex:
|
228
|
-
# <?include xl:href="root/myfile.sx" ?>
|
229
|
-
#
|
230
|
-
document.gsub!(/<\?include xl:href="(.*?)"(.*)\?>/) { |match|
|
231
|
-
# gmosx: xmm match matches the whole string.
|
232
|
-
match = overload_path($1)
|
233
|
-
load_statically_included("#$root_dir/#{match}")
|
234
|
-
}
|
235
|
-
|
236
|
-
# dynamic includes
|
237
|
-
# the target file is included at run time
|
238
|
-
#
|
239
|
-
document.gsub!(/<x:include xl:href="(.*?)"(.*)(.?)\/>/) { |match|
|
240
|
-
"<?r __out << __include('#$1', request) ?>"
|
241
|
-
}
|
242
|
-
|
243
|
-
# expand method macro
|
244
|
-
#
|
245
|
-
document.gsub!(/\@\?(.*?)(['|"|\s])/, '#{_a(request, %^\1^)}\2')
|
246
|
-
|
247
|
-
# localisation
|
248
|
-
# gmosx, OPTIMIZE in the future i could pre translate the strings!
|
249
|
-
document.gsub!(/\|:(.*?)\|/, '#{lc[:\1]}')
|
250
|
-
document.gsub!(/\!:(.*?)\|(.*?)\!/, '#{lc[:\1].call(\2)}')
|
251
|
-
|
252
|
-
# extract the definition code. The <?def .. ?> will
|
253
|
-
# be ignored afterwards.
|
254
|
-
#
|
255
|
-
document.scan(/<\?def(.*?)\?>/m) { |match|
|
256
|
-
defcode << $1 << "\n"
|
257
|
-
}
|
258
|
-
|
259
|
-
# extract the initialization phase code. The <?i .. ?> will
|
260
|
-
# be ignored afterwards.
|
261
|
-
#
|
262
|
-
document.scan(/<\?i(.*?)\?>/m) { |match|
|
263
|
-
initcode << $1 << "\n"
|
264
|
-
}
|
265
|
-
|
266
|
-
rescue Exception, StandardError => e
|
267
|
-
$log.error pp_exception(e)
|
268
|
-
raise RuntimeError.new
|
269
|
-
end
|
270
|
-
|
271
|
-
# pre-transform the script.
|
272
|
-
pagecode = shader.transform(document)
|
273
|
-
|
274
|
-
# preprocess the script to convert to valid ruby code.
|
275
|
-
|
276
|
-
# strip the xml header! (interracts with the following gsub!)
|
277
|
-
# FIXME: perhaps the xslt could strip this?
|
278
|
-
pagecode.gsub!(/<\?xml.*\?>/, "")
|
279
|
-
|
280
|
-
# xform the processing instructions, use <?ruby, <?rb, <?r as
|
281
|
-
# a marker.
|
282
|
-
pagecode.gsub!(/\?>/, "\n__out << %{")
|
283
|
-
pagecode.gsub!(/<\?ruby /, "}\n")
|
284
|
-
pagecode.gsub!(/<\?ruby /, "}\n")
|
285
|
-
pagecode.gsub!(/<\?r /, "}\n")
|
286
|
-
|
287
|
-
# tml, TODO: resolve static injects! scan injects and update the metadata
|
288
|
-
# in the page-graph.
|
289
|
-
|
290
|
-
pagecode = %@__out << %{#{pagecode}}@
|
291
|
-
|
292
|
-
# gmosx: unescape quotes etc, that are escaped by the xslt processor.
|
293
|
-
# can we avoid this ???
|
294
|
-
# yes: the xslt processors escapes code in #{ } brackets, we should
|
295
|
-
# use <?= ?> brackets.
|
296
|
-
#
|
297
|
-
# The new version of render supports output buffering ala php.
|
298
|
-
# __out_buffers keeps a stack of buffers.
|
299
|
-
|
300
|
-
pagecode = CGI.unescapeHTML(pagecode)
|
301
|
-
key = calc_script_key(path, hash)
|
302
|
-
|
303
|
-
script = %{
|
304
|
-
class PageScript#{key} < PageScript
|
305
|
-
def initialize(path)
|
306
|
-
super
|
307
|
-
@key = "#{key}"
|
308
|
-
end
|
309
|
-
#{defcode}
|
310
|
-
def __init_render(request)
|
311
|
-
#{initcode}
|
312
|
-
end
|
313
|
-
def __render(request)
|
314
|
-
lc = request.locale
|
315
|
-
|
316
|
-
__out_buffers = nil
|
317
|
-
|
318
|
-
__out = ""
|
319
|
-
if request.is_top?
|
320
|
-
__out = %|<?xml version="1.0"?>
|
321
|
-
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">|
|
322
|
-
end
|
323
|
-
|
324
|
-
#{pagecode}
|
325
|
-
return __out
|
326
|
-
end
|
327
|
-
end
|
328
|
-
compiled_script = PageScript#{key}.new("#{path}")
|
329
|
-
}
|
330
|
-
|
331
|
-
# remove excessive white space
|
332
|
-
# gmosx: FIXME: i can do better here: remove leading space per
|
333
|
-
# line for example.
|
334
|
-
script.squeeze!(" \t")
|
335
|
-
|
336
|
-
# cache the transformed script.
|
337
|
-
#
|
338
|
-
dir = N::StringUtils.directory_from_path(cached_filename)
|
339
|
-
FileUtils.mkdir_p(dir)
|
340
|
-
File.open(cached_filename, "w") { |f|
|
341
|
-
f << script
|
342
|
-
}
|
343
|
-
|
344
|
-
return script, sub_scripts
|
345
|
-
end
|
346
|
-
|
347
|
-
# try to get the script from the cache. invalidates the compiled version and return nil
|
348
|
-
# if the script is modified since compile time. Also takes active shader and localization
|
349
|
-
# into account. If script is not compiled, transform and compile it.
|
350
|
-
#
|
351
|
-
# Output:
|
352
|
-
# the compiled script. Throws exception if the script does not exist.
|
353
|
-
#
|
354
|
-
def get_compiled_script(path, hash, shader)
|
355
|
-
compiled_script = nil
|
356
|
-
key = calc_script_key(path, hash)
|
357
|
-
|
358
|
-
# gmosx: $reload_scripts is typically set to true when debugging
|
359
|
-
# statically included files (.ss)
|
360
|
-
unless $reload_scripts
|
361
|
-
|
362
|
-
compiled_script = @@compiled_script_cache[key]
|
363
|
-
|
364
|
-
# gmosx: monitor scripts should be explicit!
|
365
|
-
if $srv_monitor_scripts and compiled_script and (File.mtime(compiled_script.path) > compiled_script.__create_time)
|
366
|
-
$log.debug "Script '#{path}' externaly modified, recompiling" if $DBG
|
367
|
-
compiled_script = nil
|
368
|
-
end
|
369
|
-
|
370
|
-
end
|
371
|
-
|
372
|
-
unless compiled_script
|
373
|
-
# the script is not cached, so load, transform and compile it!
|
374
|
-
$log.debug "Compiling script '#{key}'" if $DBG
|
375
|
-
|
376
|
-
script, sub_scripts = transform_script(path, hash, shader)
|
377
|
-
compiled_script = compile_script(script)
|
378
|
-
compiled_script.sub_scripts = sub_scripts unless sub_scripts.empty?
|
379
|
-
|
380
|
-
@@compiled_script_cache[key] = compiled_script
|
381
|
-
end
|
382
|
-
|
383
|
-
return compiled_script
|
384
|
-
end
|
385
|
-
|
386
|
-
#-----------------------------------------------------------------------------
|
387
|
-
# Hooks
|
388
|
-
|
389
|
-
# Calculate shader for this request.
|
390
|
-
#
|
391
|
-
# TODO: allow per include shader!
|
392
|
-
#
|
393
|
-
def calc_shader(request)
|
394
|
-
return $default_shader
|
395
|
-
# return $shaders[request.user.shader]
|
396
|
-
end
|
397
|
-
|
398
|
-
# Standard encoding/modification/customization of the fragment_hash.
|
399
|
-
# Override this in your application to provide customized encoding.
|
400
|
-
# For example encode the shader id.
|
401
|
-
#
|
402
|
-
def calc_tag(request)
|
403
|
-
return "#{request.locale[:locale]}#{request.shader.name}"
|
404
|
-
end
|
405
|
-
|
406
|
-
def calc_script_key(path, hash)
|
407
|
-
return "#{path}#{hash}".gsub(/[^\w]/, "")
|
408
|
-
end
|
409
|
-
|
410
|
-
#-----------------------------------------------------------------------------
|
411
|
-
# Utils
|
412
|
-
|
413
|
-
# loads a script and prepares it for statically inclusion by removing
|
414
|
-
# the optional xml prologue/epilogue.
|
415
|
-
#
|
416
|
-
def load_statically_included(filename)
|
417
|
-
$log.debug "Statically including '#{filename}'" if $DBG
|
418
|
-
|
419
|
-
code = File.read(filename)
|
420
|
-
code.gsub!(/<\?xml.*\?>/, "")
|
421
|
-
code.gsub!(/<\/?root(.*?)>/m, " ");
|
422
|
-
|
423
|
-
return code
|
424
|
-
end
|
425
|
-
|
426
|
-
end # PageHandler
|
427
|
-
|
428
|
-
# = Pagescript
|
429
|
-
#
|
430
|
-
# encapsulates the script defining a specific xml page.
|
431
|
-
# effectively acts as a Generator.
|
432
|
-
#
|
433
|
-
# === Requirements:
|
434
|
-
#
|
435
|
-
# - the generated class should be a singleton, no need to stress
|
436
|
-
# the garbage collector.
|
437
|
-
# - the render method should be thread safe.
|
438
|
-
#
|
439
|
-
# == Design:
|
440
|
-
#
|
441
|
-
# - only pass request and request for simplicity
|
442
|
-
# __session can be deducted from request, and __user/__errors from
|
443
|
-
# __session.
|
444
|
-
#
|
445
|
-
# === Injection:
|
446
|
-
#
|
447
|
-
# Injection is the inclusion of subscripts in a super (parent) script.
|
448
|
-
# There are two types of inclusion, dynamic and static. In contrary to what
|
449
|
-
# you may believe, dynamix inclusion IS needed in cases where you decide
|
450
|
-
# what tou include at runtime. A synthesized "my" page is a good example.
|
451
|
-
# However, n1 overused dynamic inclusion. In most cases static inclusion
|
452
|
-
# works just fine. Another interesting optimization is when you dynamically
|
453
|
-
# include but not recursivelly. The full, recursive and dynamic include is
|
454
|
-
# really seldomly needed. However in our implementation there is no difference
|
455
|
-
# between inject_once / inject_recursive.
|
456
|
-
#
|
457
|
-
# === Class generation example:
|
458
|
-
#
|
459
|
-
# WARNING: the example may be deprecated!
|
460
|
-
#
|
461
|
-
# source script:
|
462
|
-
#
|
463
|
-
# <?xml version='1.0'?>
|
464
|
-
# <?ruby
|
465
|
-
# a = 5
|
466
|
-
# b = request["bval"]
|
467
|
-
# ?>
|
468
|
-
# <html>
|
469
|
-
# <body>
|
470
|
-
# this is it a = #{a}, b = #{b} and c = #{request["cval"]}<br/>
|
471
|
-
# cool huh?
|
472
|
-
# </body>
|
473
|
-
# </html>
|
474
|
-
#
|
475
|
-
# generated class:
|
476
|
-
#
|
477
|
-
# class PageScript__index
|
478
|
-
# def render(request)
|
479
|
-
# out = ""
|
480
|
-
# a = 5
|
481
|
-
# b = request["bval"]
|
482
|
-
# __out << %{
|
483
|
-
# <html>
|
484
|
-
# <body>
|
485
|
-
# this is it a = #{a}, b = #{b} and c = #{request["cval"]}<br/>
|
486
|
-
# cool huh?
|
487
|
-
# </body>
|
488
|
-
# </html>
|
489
|
-
# }
|
490
|
-
# return __out
|
491
|
-
# end
|
492
|
-
# end
|
493
|
-
#
|
494
|
-
# === Future:
|
495
|
-
#
|
496
|
-
# - use catbuffer for optimized appends.
|
497
|
-
#
|
498
|
-
class PageScript < Script
|
499
|
-
# <x:include xl:href='...'/> implementation
|
500
|
-
# Dynamically include ("inject") a subpage (fragment) in this page.
|
501
|
-
#
|
502
|
-
# gmosx, SOS: This is a new version for dynamic inclusion that doesnt
|
503
|
-
# generate subrequest objects. For use with new (v3) code.
|
504
|
-
#
|
505
|
-
# === Design:
|
506
|
-
#
|
507
|
-
# To keep this method simple (and as a small optimization) we used
|
508
|
-
# to not allow a query string when including. Pass the parameters
|
509
|
-
# as request arguments or parameters. Here is an example:
|
510
|
-
#
|
511
|
-
# <?r request_set_arg("max_message", 5); request["admin"] = true ?>
|
512
|
-
# <x:include xl:href='*parts/messages/view-messages.xi'/>
|
513
|
-
#
|
514
|
-
# However to support legacy code, to follow the REST design guidelines,
|
515
|
-
# and because it turned out to be easy thanks to the refactoring, the
|
516
|
-
# query string is supported. Passing params through the request object
|
517
|
-
# is suggested though (and essential to pass ruby objects). The query
|
518
|
-
# string of the included string is converted to arguments to avoid
|
519
|
-
# poluting the parent request query string.
|
520
|
-
#
|
521
|
-
# Still, the preferred way is to use the args hash to pass special
|
522
|
-
# arguments to included scripts.
|
523
|
-
# <?r request.set_arg("maxitems", 5) ?>
|
524
|
-
# <x:include xl:href="p/list.si" />
|
525
|
-
#
|
526
|
-
# If you want to include the parents query string for caching purposes
|
527
|
-
# call with parent = true!
|
528
|
-
#
|
529
|
-
# TODO: add unit test for this.
|
530
|
-
#
|
531
|
-
def __include(uri, request, parent = false)
|
532
|
-
begin
|
533
|
-
$log.devel "Including #{uri}"
|
534
|
-
|
535
|
-
# get script real path and type
|
536
|
-
script_path, type, args, qs = N::UriUtils.decode(uri)
|
537
|
-
|
538
|
-
# update the dependencies set, we only need the key as flag,
|
539
|
-
# so just insert true. Calculating the dependencies this
|
540
|
-
# way is simple and not too inefficient.
|
541
|
-
#
|
542
|
-
# @dependencies[script_path] = true
|
543
|
-
|
544
|
-
request.level += 1
|
545
|
-
request.path = script_path
|
546
|
-
|
547
|
-
# Build the cache key for this request.
|
548
|
-
# attach the query string of the parent. Needed for
|
549
|
-
# example in fragments with pagers. Do NOT do this
|
550
|
-
# by default, or excessive numbers of fragments will
|
551
|
-
# be generated.
|
552
|
-
if parent
|
553
|
-
request.fragment_hash = "#{qs}#{request.query_string}"
|
554
|
-
else
|
555
|
-
request.fragment_hash = qs
|
556
|
-
end
|
557
|
-
|
558
|
-
# add arguments passes through the query string.
|
559
|
-
request.parameters.update(args)
|
560
|
-
|
561
|
-
# TODO: select by regexp
|
562
|
-
# FIXME: hacky implementation, NO Need for this test, always
|
563
|
-
# PageHandler.
|
564
|
-
if handler = $srv_extension_map[type]
|
565
|
-
handler = handler[1]
|
566
|
-
else
|
567
|
-
$log.error "No handler for extension '#{type}'"
|
568
|
-
unless N::StringUtils.valid?(type)
|
569
|
-
$log.error "Perhaps you have a syntax error in your include statement or you didnt pass the uri"
|
570
|
-
end
|
571
|
-
end
|
572
|
-
|
573
|
-
fragment, script = handler.sub_process(request)
|
574
|
-
|
575
|
-
# restore parent request
|
576
|
-
request.level -= 1
|
577
|
-
|
578
|
-
# gmosx: one idea was to clear the args here to avoid a class of bugs.
|
579
|
-
# But i think it is better to trust the developer to do this, thus
|
580
|
-
# giving him greater flexibility.
|
581
|
-
# gmosx: NO !!! practice shows that it is NOT GOOD to trust the
|
582
|
-
# developer :)
|
583
|
-
# Hmm clearing the args also poses problems though :(
|
584
|
-
#
|
585
|
-
# request.args.clear()
|
586
|
-
|
587
|
-
return fragment.body
|
588
|
-
|
589
|
-
rescue ScriptHandlerError => e
|
590
|
-
# allready handled!
|
591
|
-
rescue Exception, StandardError => e
|
592
|
-
# gmosx: this block is used to catch possible errors in the inject
|
593
|
-
# implementation if we do not catch these errors here, they
|
594
|
-
# propagate to the caller that prints a NON usefull message, that
|
595
|
-
# can waste a developers time.
|
596
|
-
#
|
597
|
-
$log.error "error in INCLUDE IMPLEMENTATION while including: #{uri}"
|
598
|
-
# gmosx: too noisy, only uncomment if you get the above error!!
|
599
|
-
$log.error pp_exception(e)
|
600
|
-
|
601
|
-
# Following our design goal of chaos reduction (ie small changes
|
602
|
-
# should result in small results) we drink the exception and
|
603
|
-
# output an error flag. So the rest of the page is rendered and
|
604
|
-
# the server error handler is NOT triggered.
|
605
|
-
end
|
606
|
-
|
607
|
-
return "(error)"
|
608
|
-
end
|
609
|
-
|
610
|
-
end # PageScript
|
611
|
-
|
612
|
-
end # module
|