nitro 0.29.0 → 0.30.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +410 -0
- data/ProjectInfo +36 -44
- data/README +5 -5
- data/doc/AUTHORS +6 -0
- data/doc/RELEASES +159 -2
- data/lib/glue/sweeper.rb +2 -2
- data/lib/glue/webfile.rb +14 -1
- data/lib/nitro.rb +6 -9
- data/lib/nitro/adapter/mongrel.rb +36 -43
- data/lib/nitro/adapter/scgi.rb +1 -1
- data/lib/nitro/adapter/webrick.rb +96 -24
- data/lib/nitro/caching/actions.rb +2 -1
- data/lib/nitro/caching/fragments.rb +1 -8
- data/lib/nitro/caching/output.rb +14 -4
- data/lib/nitro/cgi.rb +19 -21
- data/lib/nitro/cgi/cookie.rb +5 -1
- data/lib/nitro/cgi/request.rb +20 -4
- data/lib/nitro/compiler.rb +74 -28
- data/lib/nitro/compiler/cleanup.rb +1 -1
- data/lib/nitro/compiler/elements.rb +1 -2
- data/lib/nitro/compiler/localization.rb +1 -1
- data/lib/nitro/compiler/markup.rb +1 -1
- data/lib/nitro/compiler/script.rb +52 -44
- data/lib/nitro/compiler/squeeze.rb +4 -3
- data/lib/nitro/compiler/xslt.rb +7 -6
- data/lib/nitro/context.rb +39 -20
- data/lib/nitro/controller.rb +24 -5
- data/lib/nitro/dispatcher.rb +13 -5
- data/lib/nitro/global.rb +63 -0
- data/lib/nitro/helper/feed.rb +432 -0
- data/lib/nitro/helper/form.rb +11 -3
- data/lib/nitro/helper/form/builder.rb +140 -0
- data/lib/nitro/helper/form/controls.rb +2 -1
- data/lib/nitro/helper/javascript.rb +6 -0
- data/lib/nitro/helper/javascript/morphing.rb +13 -6
- data/lib/nitro/helper/xhtml.rb +42 -6
- data/lib/nitro/helper/xml.rb +3 -0
- data/lib/nitro/part.rb +2 -2
- data/lib/nitro/render.rb +7 -2
- data/lib/nitro/router.rb +57 -16
- data/lib/nitro/scaffolding.rb +29 -20
- data/lib/nitro/server.rb +4 -10
- data/lib/nitro/server/drb.rb +1 -1
- data/lib/nitro/server/runner.rb +10 -0
- data/lib/nitro/session.rb +31 -12
- data/lib/nitro/session/drb.rb +13 -1
- data/lib/nitro/session/file.rb +1 -1
- data/lib/nitro/session/memcached.rb +1 -1
- data/lib/nitro/session/memory.rb +1 -1
- data/lib/nitro/session/og.rb +1 -1
- data/lib/nitro/test/testcase.rb +3 -0
- data/proto/public/error.xhtml +5 -5
- data/proto/public/js/controls.js +2 -2
- data/proto/public/js/dragdrop.js +320 -79
- data/proto/public/js/effects.js +200 -152
- data/proto/public/js/prototype.js +284 -63
- data/proto/public/js/scriptaculous.js +7 -5
- data/proto/public/js/unittest.js +11 -0
- data/proto/public/scaffold/advanced_search.xhtml +30 -0
- data/proto/public/scaffold/list.xhtml +8 -1
- data/proto/public/scaffold/search.xhtml +2 -1
- data/proto/script/scgi_service +1 -1
- data/src/part/admin/controller.rb +1 -1
- data/src/part/admin/skin.rb +1 -1
- data/test/nitro/CONFIG.rb +3 -0
- data/test/nitro/adapter/tc_webrick.rb +1 -1
- data/test/nitro/cgi/tc_cookie.rb +1 -1
- data/test/nitro/cgi/tc_request.rb +5 -5
- data/test/nitro/compiler/tc_client_morpher.rb +47 -0
- data/test/nitro/compiler/tc_compiler.rb +2 -0
- data/test/nitro/helper/tc_feed.rb +138 -0
- data/test/nitro/helper/tc_pager.rb +1 -1
- data/test/nitro/helper/tc_rss.rb +1 -1
- data/test/nitro/helper/tc_table.rb +1 -1
- data/test/nitro/helper/tc_xhtml.rb +1 -1
- data/test/nitro/tc_caching.rb +1 -1
- data/test/nitro/tc_cgi.rb +1 -1
- data/test/nitro/tc_context.rb +1 -1
- data/test/nitro/tc_controller.rb +31 -3
- data/test/nitro/tc_controller_aspect.rb +1 -1
- data/test/nitro/tc_dispatcher.rb +1 -1
- data/test/nitro/tc_element.rb +1 -1
- data/test/nitro/tc_flash.rb +1 -1
- data/test/nitro/tc_helper.rb +1 -1
- data/test/nitro/tc_render.rb +6 -6
- data/test/nitro/tc_router.rb +8 -4
- data/test/nitro/tc_server.rb +1 -3
- data/test/nitro/tc_session.rb +1 -3
- metadata +107 -104
- data/Rakefile +0 -232
- data/lib/nitro/adapter/acgi.rb +0 -237
- data/proto/public/Makefile.acgi +0 -40
- data/proto/public/acgi.c +0 -138
@@ -8,7 +8,7 @@ module Nitro
|
|
8
8
|
|
9
9
|
module ScriptCompiler
|
10
10
|
extend JavascriptHelper
|
11
|
-
|
11
|
+
|
12
12
|
def self.transform(text, compiler)
|
13
13
|
begin
|
14
14
|
client = constant("#{compiler.controller}::Client")
|
@@ -21,9 +21,11 @@ module ScriptCompiler
|
|
21
21
|
client = client.new
|
22
22
|
|
23
23
|
functions = {}
|
24
|
+
params = {}
|
24
25
|
|
25
|
-
text.scan(/__nc_(.*)\(\)/) do |match|
|
26
|
+
text.scan(/__nc_(.*)\((.*)\)/) do |match|
|
26
27
|
functions[match.first] = true
|
28
|
+
params[match.last] = true
|
27
29
|
end
|
28
30
|
|
29
31
|
script = ''
|
@@ -38,13 +40,13 @@ module ScriptCompiler
|
|
38
40
|
|
39
41
|
function_code = client.buffer
|
40
42
|
|
41
|
-
# if the client action
|
43
|
+
# if the client action returns a String append it to
|
42
44
|
# the function code.
|
43
45
|
|
44
46
|
function_code << ret if ret.is_a?(String)
|
45
47
|
|
46
48
|
script << %{
|
47
|
-
function __nc_#{fun}() {
|
49
|
+
function __nc_#{fun}(params) {
|
48
50
|
#{function_code}
|
49
51
|
}
|
50
52
|
}
|
@@ -53,51 +55,57 @@ module ScriptCompiler
|
|
53
55
|
end
|
54
56
|
end
|
55
57
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
58
|
+
end
|
59
|
+
|
60
|
+
# Inject css in the head tag.
|
61
|
+
|
62
|
+
if css_buffer = compiler.shared[:css_buffer]
|
63
|
+
text.sub!(/<\/head>/) do |match|
|
64
|
+
%{
|
65
|
+
<style>
|
66
|
+
#{css_buffer}
|
67
|
+
</style>
|
68
|
+
</head>
|
69
|
+
}
|
67
70
|
end
|
71
|
+
end
|
68
72
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
73
|
+
# Inject required javascript files in the head tag.
|
74
|
+
|
75
|
+
required_files = Javascript.required_files
|
76
|
+
if crf = compiler.shared[:js_required_files]
|
77
|
+
required_files.concat(crf.keys)
|
78
|
+
end
|
79
|
+
|
80
|
+
unless required_files.empty?
|
81
|
+
text.sub!(/<\/head>/) do |match|
|
82
|
+
%{
|
83
|
+
#{include_script *required_files}
|
84
|
+
</head>
|
85
|
+
}
|
78
86
|
end
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
87
|
+
end
|
88
|
+
|
89
|
+
# Inject javascript just before the end of the
|
90
|
+
# template.
|
91
|
+
#--
|
92
|
+
# gmosx: injection happens at the end, so that the DOM
|
93
|
+
# tree is created. Dunno if this is valid xhtml though.
|
94
|
+
#++
|
86
95
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
end
|
96
|
+
js_buffer = compiler.shared[:js_buffer]
|
97
|
+
|
98
|
+
if script or js_buffer
|
99
|
+
text.sub!(/<\/body>/) do |match|
|
100
|
+
%{
|
101
|
+
<script type="text/javascript">
|
102
|
+
#{script}
|
103
|
+
#{js_buffer}
|
104
|
+
</script>
|
105
|
+
</body>
|
106
|
+
}
|
99
107
|
end
|
100
|
-
end
|
108
|
+
end
|
101
109
|
|
102
110
|
return text
|
103
111
|
end
|
@@ -1,12 +1,13 @@
|
|
1
1
|
module Nitro
|
2
|
-
|
3
|
-
#
|
2
|
+
|
3
|
+
# A compiler transformation pipeline stage that compresses
|
4
|
+
# the generated xhtml code.
|
4
5
|
|
5
6
|
class Squeeze
|
6
7
|
|
7
8
|
# Compresses the inline xhtml. Does not touch the ruby code.
|
8
9
|
|
9
|
-
def self.transform(text)
|
10
|
+
def self.transform(text, compiler = nil)
|
10
11
|
return text.gsub(/\@out \<\< \%\^(.*?)\^/m) do |match|
|
11
12
|
c = $1.gsub(/^(\s*)/m, '').squeeze(" \t").tr("\n", '').tr("\t", ' ')
|
12
13
|
"@out << %{#{c}}"
|
data/lib/nitro/compiler/xslt.rb
CHANGED
@@ -4,10 +4,12 @@ require 'xml/xslt'
|
|
4
4
|
|
5
5
|
module Nitro
|
6
6
|
|
7
|
-
#
|
7
|
+
# Pre-applies an XSL transformation to the template file to
|
8
|
+
# generate the final template file. Due to preproccessing, the
|
9
|
+
# transformation comes for free.
|
8
10
|
|
9
11
|
class XSLTransform
|
10
|
-
|
12
|
+
@@sync = Sync.new
|
11
13
|
|
12
14
|
class << self
|
13
15
|
|
@@ -34,8 +36,7 @@ class XSLTransform
|
|
34
36
|
|
35
37
|
# Transform the given xml.
|
36
38
|
|
37
|
-
def transform(text)
|
38
|
-
|
39
|
+
def transform(text, compiler = nil)
|
39
40
|
parse_xsl()
|
40
41
|
@xslt.xml = text
|
41
42
|
hash += @name
|
@@ -43,8 +44,8 @@ class XSLTransform
|
|
43
44
|
process_next(hash, xslt.serve)
|
44
45
|
end
|
45
46
|
|
46
|
-
|
47
|
-
|
47
|
+
private
|
48
|
+
|
48
49
|
# Parse the xsl.
|
49
50
|
|
50
51
|
def parse_xsl
|
data/lib/nitro/context.rb
CHANGED
@@ -2,10 +2,13 @@ require 'forwardable'
|
|
2
2
|
|
3
3
|
require 'facet/kernel/assign_with'
|
4
4
|
|
5
|
+
require 'glue/cache/memory'
|
6
|
+
|
5
7
|
require 'nitro/cgi'
|
6
8
|
require 'nitro/cgi/request'
|
7
9
|
require 'nitro/cgi/response'
|
8
10
|
require 'nitro/render'
|
11
|
+
require 'nitro/global'
|
9
12
|
require 'nitro/session'
|
10
13
|
|
11
14
|
module Nitro
|
@@ -16,17 +19,14 @@ module Nitro
|
|
16
19
|
#
|
17
20
|
# The Context object can be accessed by the context, request or
|
18
21
|
# response aliases. You can use the alias that makes sense
|
19
|
-
# every time.
|
22
|
+
# every time. This means inside an action request, response and
|
23
|
+
# context all point to the same object.
|
20
24
|
|
21
25
|
class Context
|
22
26
|
include Request
|
23
27
|
include Response
|
24
28
|
include Render
|
25
29
|
|
26
|
-
# The cache/store used to back global variables.
|
27
|
-
|
28
|
-
setting :global_cache_class, :default => ($NITRO_GLOBAL_CACHE_CLASS || MemoryCache), :doc => 'The store used to back global variables'
|
29
|
-
|
30
30
|
# The configuration parameters.
|
31
31
|
|
32
32
|
attr_accessor :conf
|
@@ -42,7 +42,15 @@ class Context
|
|
42
42
|
|
43
43
|
attr_accessor :dispatcher
|
44
44
|
|
45
|
+
# The rendering level. An action may include sub-actions,
|
46
|
+
# each time the action is called, the level is increased,
|
47
|
+
# when the action returns the level decreases. The first
|
48
|
+
# action, called top level action has a level of 1.
|
49
|
+
|
50
|
+
attr_accessor :level
|
51
|
+
|
45
52
|
def initialize(conf)
|
53
|
+
@level = 0
|
46
54
|
@conf = conf
|
47
55
|
@dispatcher = @conf.dispatcher
|
48
56
|
@context = self
|
@@ -57,6 +65,17 @@ class Context
|
|
57
65
|
@out ||= OutputBuffer.new
|
58
66
|
end
|
59
67
|
|
68
|
+
|
69
|
+
# Don't sync session. This method may be needed in low level
|
70
|
+
# hacks with the session code. For example if you updade an
|
71
|
+
# entity (managed) object that is cached in the session, you
|
72
|
+
# may dissable the syncing to avoid resetting the old value
|
73
|
+
# after the sync.
|
74
|
+
|
75
|
+
def no_sync!
|
76
|
+
@no_sync = true
|
77
|
+
end
|
78
|
+
|
60
79
|
# Close the context, should be called at the
|
61
80
|
# end of the HTTP request handling code.
|
62
81
|
|
@@ -67,12 +86,14 @@ class Context
|
|
67
86
|
@session[:FLASH].clean
|
68
87
|
end
|
69
88
|
|
70
|
-
# INVESTIGATE: is this needed?
|
71
|
-
@session.sync
|
89
|
+
# INVESTIGATE: is this needed?
|
90
|
+
@session.sync unless @no_sync
|
72
91
|
end
|
73
92
|
end
|
74
93
|
alias_method :finish, :close
|
75
94
|
|
95
|
+
# The accomulated output for the current context (ie the
|
96
|
+
# current request).
|
76
97
|
#--
|
77
98
|
# FIXME: still something more elegant/efficient.
|
78
99
|
#++
|
@@ -97,7 +118,7 @@ class Context
|
|
97
118
|
# these variables can reside outside of the process.
|
98
119
|
|
99
120
|
def global
|
100
|
-
return
|
121
|
+
return Global
|
101
122
|
end
|
102
123
|
alias_method :application, :global
|
103
124
|
|
@@ -132,18 +153,16 @@ class Context
|
|
132
153
|
alias_method :populate, :fill
|
133
154
|
alias_method :assign, :fill
|
134
155
|
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
def_delegators :@context, :controller, :request, :response, :session, :out
|
156
|
+
# Is the current action the top level action? The level
|
157
|
+
# of the top action is 1.
|
158
|
+
|
159
|
+
def is_top_level?
|
160
|
+
@level == 1
|
161
|
+
end
|
162
|
+
alias_method :is_top?, :is_top_level?
|
163
|
+
alias_method :in_top_level?, :is_top_level?
|
164
|
+
alias_method :top_level?, :is_top_level?
|
165
|
+
|
147
166
|
end
|
148
167
|
|
149
168
|
end
|
data/lib/nitro/controller.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'facet/annotation'
|
2
2
|
require 'facet/inheritor'
|
3
|
+
require 'facets/more/aspects'
|
3
4
|
|
4
|
-
require 'glue/aspects'
|
5
5
|
require 'glue/markup'
|
6
6
|
|
7
7
|
require 'nitro'
|
@@ -37,12 +37,13 @@ module Nitro
|
|
37
37
|
|
38
38
|
|
39
39
|
module Publishable
|
40
|
+
|
40
41
|
def self.included(base)
|
41
42
|
super
|
42
43
|
|
43
44
|
base.module_eval do
|
44
45
|
include Render
|
45
|
-
include
|
46
|
+
include ::Aspects
|
46
47
|
include Flashing
|
47
48
|
include Helpers
|
48
49
|
end
|
@@ -154,6 +155,13 @@ module Publishable
|
|
154
155
|
@template_root.reverse!
|
155
156
|
end
|
156
157
|
alias_method :mount, :mount_at
|
158
|
+
|
159
|
+
# Returns the path where this controller is mounted.
|
160
|
+
|
161
|
+
def mount_path
|
162
|
+
@mount_path
|
163
|
+
end
|
164
|
+
alias_method :mount_point, :mount_path
|
157
165
|
end
|
158
166
|
end
|
159
167
|
|
@@ -170,10 +178,21 @@ private
|
|
170
178
|
@context.cookies
|
171
179
|
end
|
172
180
|
|
181
|
+
# Send the cookie to the response stream.
|
182
|
+
|
173
183
|
def send_cookie(name, value = nil)
|
174
184
|
@context.add_cookie(name, value)
|
175
185
|
end
|
176
186
|
|
187
|
+
# Delete the cookie by setting the expire time to now and
|
188
|
+
# clearing the value.
|
189
|
+
|
190
|
+
def delete_cookie(name)
|
191
|
+
cookie = Cookie.new(name, '')
|
192
|
+
cookie.expires = Time.now
|
193
|
+
@context.add_cookie(cookie)
|
194
|
+
end
|
195
|
+
|
177
196
|
# Encode controller, action, params into a valid url.
|
178
197
|
# Automatically respects nice urls and routing.
|
179
198
|
#
|
@@ -210,10 +229,10 @@ private
|
|
210
229
|
controller = args.shift
|
211
230
|
action = args.shift.to_sym
|
212
231
|
|
213
|
-
url = "
|
232
|
+
url = "#{controller.mount_path}/#{action}"
|
214
233
|
|
215
234
|
unless args.empty?
|
216
|
-
if controller.
|
235
|
+
if controller.respond_to_action_or_template? action
|
217
236
|
param_count = controller.instance_method(action).arity
|
218
237
|
if param_count != 0
|
219
238
|
param_count.times do
|
@@ -262,7 +281,7 @@ class Controller
|
|
262
281
|
def self.mounted(path)
|
263
282
|
# Resolve aspects.
|
264
283
|
|
265
|
-
|
284
|
+
::Aspects.include_advice_modules(self)
|
266
285
|
|
267
286
|
# The scaffolding code is compiled after the mount, so
|
268
287
|
# that template roots are finalized.
|
data/lib/nitro/dispatcher.rb
CHANGED
@@ -74,6 +74,7 @@ class Dispatcher
|
|
74
74
|
|
75
75
|
# Customize the class for mounting at the given path.
|
76
76
|
#--
|
77
|
+
# gmosx, TODO: path should include trailing '/'
|
77
78
|
# gmosx, TODO: should actually create an instance, thus
|
78
79
|
# allowing mounting the same controller to multiple
|
79
80
|
# paths, plus simplifying the code. This instance will
|
@@ -81,7 +82,6 @@ class Dispatcher
|
|
81
82
|
#++
|
82
83
|
|
83
84
|
klass.mount_at(path)
|
84
|
-
|
85
85
|
klass.mounted(path) if klass.respond_to?(:mounted)
|
86
86
|
end
|
87
87
|
|
@@ -112,16 +112,23 @@ class Dispatcher
|
|
112
112
|
|
113
113
|
# Update the routes. Typically called after a new
|
114
114
|
# Controller is mounted.
|
115
|
+
#
|
116
|
+
# === Example of routing through annotations
|
117
|
+
#
|
118
|
+
# def view_user
|
119
|
+
# "params: #{request[:id]} and #{request[:mode]}"
|
120
|
+
# end
|
121
|
+
# ann :view_user, :route => [ /user_(\d*)_(*?)\.html/, :id, :mode ]
|
115
122
|
|
116
123
|
def update_routes
|
117
|
-
|
124
|
+
init_routes()
|
118
125
|
|
119
126
|
@controllers.each do |base, c|
|
120
127
|
base = '' if base == '/'
|
121
128
|
for m in c.action_methods
|
122
129
|
m = m.to_sym
|
123
130
|
if route = c.ann(m).route and (!route.nil?)
|
124
|
-
add_route(route.first, c, m, route.last)
|
131
|
+
add_route(route.first, :controller => c, :action => m, :params => route.last)
|
125
132
|
end
|
126
133
|
end
|
127
134
|
end
|
@@ -156,8 +163,9 @@ class Dispatcher
|
|
156
163
|
|
157
164
|
klass, action, params = decode_route(path)
|
158
165
|
if klass
|
159
|
-
context.params.update(params)
|
160
|
-
|
166
|
+
context.params.update(params) if params
|
167
|
+
# gmosx, FIXME/OPTIMIZE: no annotation for mount point!!
|
168
|
+
return klass, "#{action}_action", klass.mount_path
|
161
169
|
end
|
162
170
|
|
163
171
|
parts = path.split('/')
|
data/lib/nitro/global.rb
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'glue/cache/memory'
|
2
|
+
|
3
|
+
# Global scoped variables. This is backed by a Cache store.
|
4
|
+
|
5
|
+
class Global
|
6
|
+
|
7
|
+
# The address of the store.
|
8
|
+
|
9
|
+
setting :cache_address, :default => '127.0.0.1', :doc => 'The address of the store'
|
10
|
+
|
11
|
+
# The port of the store.
|
12
|
+
|
13
|
+
setting :cache_port, :default => 9079, :doc => 'The port of the store'
|
14
|
+
|
15
|
+
# The cache store that backs global variables.
|
16
|
+
|
17
|
+
setting :cache, :default => ::Glue::MemoryCache.new, :doc => 'The cache store that backs global variables'
|
18
|
+
|
19
|
+
class << self
|
20
|
+
|
21
|
+
# Initialize a global value once.
|
22
|
+
|
23
|
+
def init(key, value)
|
24
|
+
unless Global[key]
|
25
|
+
Global[key] = value
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def set(key, value)
|
30
|
+
Global.cache[key] = value
|
31
|
+
end
|
32
|
+
alias_method :[]=, :set
|
33
|
+
|
34
|
+
def get(key)
|
35
|
+
return Global.cache[key]
|
36
|
+
end
|
37
|
+
alias_method :[], :get
|
38
|
+
|
39
|
+
# If block is given it acts as an update methods,
|
40
|
+
# that transparently handles distributed stores.
|
41
|
+
#
|
42
|
+
# Global.update(:USERS) do |users|
|
43
|
+
# users << 'gmosx'
|
44
|
+
# end
|
45
|
+
|
46
|
+
def update(key)
|
47
|
+
if block_given?
|
48
|
+
# update, also handles distributed stores.
|
49
|
+
val = Global.cache[key]
|
50
|
+
yield val
|
51
|
+
Global.cache[key] = val
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def delete(key)
|
56
|
+
Global.cache.delete(key)
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
|
63
|
+
# * George Moschovitis <gm@navel.gr>
|