nitro 0.30.0 → 0.31.0
Sign up to get free protection for your applications and to get access to all the features.
- data/ProjectInfo +6 -6
- data/bin/nitro +100 -7
- data/doc/RELEASES +18 -0
- data/lib/glue/sweeper.rb +9 -5
- data/lib/nitro.rb +13 -13
- data/lib/nitro/adapter/console.rb +7 -0
- data/lib/nitro/adapter/mongrel.rb +34 -71
- data/lib/nitro/adapter/script.rb +71 -0
- data/lib/nitro/adapter/webrick.rb +0 -2
- data/lib/nitro/caching.rb +0 -1
- data/lib/nitro/caching/fragments.rb +2 -3
- data/lib/nitro/caching/output.rb +3 -4
- data/lib/nitro/cgi.rb +14 -0
- data/lib/nitro/cgi/request.rb +10 -9
- data/lib/nitro/cgi/sendfile.rb +45 -0
- data/lib/nitro/compiler.rb +1 -1
- data/lib/nitro/compiler/errors.rb +3 -3
- data/lib/nitro/context.rb +16 -1
- data/lib/nitro/controller.rb +60 -5
- data/lib/nitro/dispatcher.rb +2 -1
- data/lib/nitro/helper.rb +0 -2
- data/lib/nitro/helper/form/builder.rb +5 -1
- data/lib/nitro/helper/javascript.rb +1 -1
- data/lib/nitro/helper/pager.rb +19 -10
- data/lib/nitro/helper/table.rb +28 -3
- data/lib/nitro/helper/xhtml.rb +4 -3
- data/lib/nitro/part.rb +57 -2
- data/lib/nitro/render.rb +67 -28
- data/lib/nitro/router.rb +1 -0
- data/lib/nitro/scaffold.rb +141 -0
- data/lib/nitro/scaffolding.rb +17 -17
- data/lib/nitro/server/runner.rb +2 -9
- data/lib/nitro/session.rb +13 -5
- data/lib/nitro/test/testcase.rb +2 -0
- data/proto/run.rb +3 -1
- data/src/part/admin/controller.rb +2 -1
- data/src/part/admin/template/index.xhtml +2 -2
- data/test/nitro/tc_element.rb +13 -11
- data/test/nitro/tc_render.rb +1 -2
- metadata +186 -182
- data/bin/nitrogen +0 -5
- data/lib/nitro/helper/wee.rb +0 -57
data/lib/nitro/helper/pager.rb
CHANGED
@@ -24,6 +24,10 @@ class Pager
|
|
24
24
|
|
25
25
|
setting :key, :default => '_page', :doc => 'The request key'
|
26
26
|
|
27
|
+
# The nav link titles
|
28
|
+
|
29
|
+
setting :link_titles, :default => {:first => 'First', :previous => 'Previous', :last => 'Last', :next => 'Next'}, :doc => "Titles of pager links"
|
30
|
+
|
27
31
|
# The current page.
|
28
32
|
|
29
33
|
attr_accessor :page
|
@@ -40,10 +44,10 @@ class Pager
|
|
40
44
|
|
41
45
|
attr_accessor :total_count
|
42
46
|
|
43
|
-
def initialize(request, per_page, total_count, key = Pager.key)
|
47
|
+
def initialize(request, per_page, total_count, key = Pager.key, link_titles = Pager.link_titles)
|
44
48
|
raise 'per_page should be > 0' unless per_page > 0
|
45
49
|
|
46
|
-
@request, @key = request, key
|
50
|
+
@request, @key, @link_titles = request, key, link_titles
|
47
51
|
@page = request.query.fetch(key, 1).to_i
|
48
52
|
@per_page = per_page
|
49
53
|
set_count(total_count)
|
@@ -55,6 +59,10 @@ class Pager
|
|
55
59
|
@page_count = (@total_count.to_f / @per_page).ceil
|
56
60
|
end
|
57
61
|
|
62
|
+
def has_pages?
|
63
|
+
return @page_count > 1
|
64
|
+
end
|
65
|
+
|
58
66
|
# Return the first page index.
|
59
67
|
|
60
68
|
def first_page
|
@@ -188,17 +196,18 @@ class Pager
|
|
188
196
|
def navigation
|
189
197
|
nav = ""
|
190
198
|
|
199
|
+
return nav unless has_pages?
|
191
200
|
unless first_page?
|
192
201
|
nav << %{
|
193
|
-
<div class="first"><a href="#{first_page_href}"
|
194
|
-
<div class="previous"><a href="#{previous_page_href}"
|
202
|
+
<div class="first"><a href="#{first_page_href}">#{CGI.escapeHTML(@link_titles[:first])}</a></div>
|
203
|
+
<div class="previous"><a href="#{previous_page_href}">#{CGI.escapeHTML(@link_titles[:previous])}</a></div>
|
195
204
|
}
|
196
205
|
end
|
197
206
|
|
198
207
|
unless last_page?
|
199
208
|
nav << %{
|
200
|
-
<div class="last"><a href="#{last_page_href}"
|
201
|
-
<div class="next"><a href="#{next_page_href}"
|
209
|
+
<div class="last"><a href="#{last_page_href}">#{CGI.escapeHTML(@link_titles[:last])}</a></div>
|
210
|
+
<div class="next"><a href="#{next_page_href}">#{CGI.escapeHTML(@link_titles[:next])}</a></div>
|
202
211
|
}
|
203
212
|
end
|
204
213
|
|
@@ -270,24 +279,24 @@ private
|
|
270
279
|
def paginate(items, options = {})
|
271
280
|
per_page = options.delete(:per_page) || Pager.per_page
|
272
281
|
pager_key = options.delete(:pager_key) || Pager.key
|
273
|
-
|
282
|
+
pager_link_titles = Pager.link_titles.merge(options.delete(:pager_link_titles) || Pager.link_titles)
|
274
283
|
case items
|
275
284
|
when Array
|
276
285
|
items = items.dup
|
277
|
-
pager = Pager.new(request, per_page, items.size, pager_key)
|
286
|
+
pager = Pager.new(request, per_page, items.size, pager_key, pager_link_titles)
|
278
287
|
items = items.slice(pager.offset, pager.per_page) || []
|
279
288
|
return items, pager
|
280
289
|
|
281
290
|
when Og::Collection
|
282
291
|
collection = items
|
283
|
-
pager = Pager.new(request, per_page, collection.count, pager_key)
|
292
|
+
pager = Pager.new(request, per_page, collection.count, pager_key, pager_link_titles)
|
284
293
|
options.update(pager.limit)
|
285
294
|
items = collection.reload(options)
|
286
295
|
return items, pager
|
287
296
|
|
288
297
|
when Class
|
289
298
|
klass = items
|
290
|
-
pager = Pager.new(request, per_page, klass.count(options), pager_key)
|
299
|
+
pager = Pager.new(request, per_page, klass.count(options), pager_key, pager_link_titles)
|
291
300
|
options.update(pager.limit)
|
292
301
|
items = klass.all(options)
|
293
302
|
return items, pager
|
data/lib/nitro/helper/table.rb
CHANGED
@@ -111,7 +111,7 @@ module TableHelper
|
|
111
111
|
str << '>'
|
112
112
|
|
113
113
|
for value in row
|
114
|
-
str << %|<td>#{value}</td>|
|
114
|
+
str << %|<td>#{process(value)}</td>|
|
115
115
|
end
|
116
116
|
|
117
117
|
str << '</tr>'
|
@@ -131,7 +131,7 @@ module TableHelper
|
|
131
131
|
str << '>'
|
132
132
|
|
133
133
|
for value in row
|
134
|
-
str << %|<td>#{value}</td>|
|
134
|
+
str << %|<td>#{process(value)}</td>|
|
135
135
|
end
|
136
136
|
|
137
137
|
str << '</tr>'
|
@@ -159,6 +159,7 @@ module TableHelper
|
|
159
159
|
str << '<tr>'
|
160
160
|
|
161
161
|
options[:headers].each_with_index do |header, index|
|
162
|
+
header = process(header)
|
162
163
|
if (options[:order] && options[:order][:values] &&
|
163
164
|
options[:order][:values][index])
|
164
165
|
order_by = options[:order][:values][index]
|
@@ -233,7 +234,31 @@ module TableHelper
|
|
233
234
|
end
|
234
235
|
|
235
236
|
def create_tbody?(options)
|
236
|
-
options[:values][0][0].respond_to?(:to_ary)
|
237
|
+
options[:values][0].respond_to?(:to_ary) && options[:values][0][0].respond_to?(:to_ary)
|
238
|
+
end
|
239
|
+
|
240
|
+
# A simple hack to allow Localization-processing in the table-helper
|
241
|
+
# overwrite this method to make this happen
|
242
|
+
# Now you can just pass [[somestring]] to the tablehelper and it will
|
243
|
+
# be localized like the rest of the page.
|
244
|
+
# ATM it's only kicking in when the @lc is set.
|
245
|
+
#
|
246
|
+
# Example for the method:
|
247
|
+
# module Nitro
|
248
|
+
# module TableHelper
|
249
|
+
# def process(value)
|
250
|
+
# val = value.to_s.gsub(/\[\[(.*?)\]\]/){ @lc[$1] }
|
251
|
+
# return val == "" ? value : val
|
252
|
+
# end
|
253
|
+
# end
|
254
|
+
# end
|
255
|
+
|
256
|
+
def process(value)
|
257
|
+
if @lc
|
258
|
+
val = value.to_s.gsub(/\[\[(.*?)\]\]/){ @lc[$1] }
|
259
|
+
return val == "" ? value : val
|
260
|
+
end
|
261
|
+
return value
|
237
262
|
end
|
238
263
|
|
239
264
|
end
|
data/lib/nitro/helper/xhtml.rb
CHANGED
@@ -13,7 +13,7 @@ module XhtmlHelper
|
|
13
13
|
elsif obj.respond_to? :to_href
|
14
14
|
href = obj.to_href
|
15
15
|
else
|
16
|
-
href = "#{obj.class.name.pluralize.
|
16
|
+
href = "#{obj.class.name.pluralize.underscore}/#{obj.oid}"
|
17
17
|
end
|
18
18
|
|
19
19
|
if base
|
@@ -63,7 +63,8 @@ module XhtmlHelper
|
|
63
63
|
|
64
64
|
values = options[:values] || options[:labels_values] || (0...labels.size).to_a
|
65
65
|
|
66
|
-
selected =
|
66
|
+
selected = options[:selected]
|
67
|
+
selected = selected.to_s if selected
|
67
68
|
|
68
69
|
labels.each_with_index do |label, idx|
|
69
70
|
value = values[idx]
|
@@ -75,7 +76,7 @@ module XhtmlHelper
|
|
75
76
|
end
|
76
77
|
style = %{ style="#{style}"}
|
77
78
|
end
|
78
|
-
if value == selected
|
79
|
+
if value.to_s == selected
|
79
80
|
str << %|<option value="#{value}" selected="selected"#{style}>#{label}</option>|
|
80
81
|
else
|
81
82
|
str << %|<option value="#{value}"#{style}>#{label}</option>|
|
data/lib/nitro/part.rb
CHANGED
@@ -3,12 +3,67 @@ module Nitro
|
|
3
3
|
# A part is a module of reusable functionality encapsulated as
|
4
4
|
# a mini site/app. You can require (include) multiple parts in
|
5
5
|
# your application. A part is in essence a high level component.
|
6
|
-
|
6
|
+
#
|
7
|
+
# The directory structure of a part mirrors the structure
|
8
|
+
# of a typicall web application. By conventions we put the
|
9
|
+
# main directories of parts in a root directory called 'part'.
|
10
|
+
#
|
11
|
+
# Let's demonstrate the above with an example. Two parts are
|
12
|
+
# defined here. A user management part (users) and a CMS
|
13
|
+
# (content). A typical dir structure goes like this ($ is a
|
14
|
+
# directory in the load path, this means you can put parts in
|
15
|
+
# multiple places as long as the are in the load path):
|
16
|
+
#
|
17
|
+
# $/part # parts will be stored here.
|
18
|
+
#
|
19
|
+
# $/part/users.rb # helper file used to 'require' the part.
|
20
|
+
# $/part/users/public/
|
21
|
+
# $/part/users/controller.rb
|
22
|
+
# $/part/users/controller/xml.rb
|
23
|
+
# $/part/users/model/user.rb
|
24
|
+
# $/part/users/model/acl.rb
|
25
|
+
# $/part/users/template/login.xhtml
|
26
|
+
# $/part/users/template/form.xinc
|
27
|
+
# $/part/users/run.rb # starts an 'example' application for this part.
|
28
|
+
#
|
29
|
+
# $/part/content.rb
|
30
|
+
# $/part/content/controller.rb
|
31
|
+
# $/part/content/model.rb
|
32
|
+
# ...
|
33
|
+
#
|
34
|
+
# Given this direcotry structure you can 'require' a part
|
35
|
+
# like this:
|
36
|
+
#
|
37
|
+
# require 'part/users'
|
38
|
+
# require 'part/content'
|
39
|
+
#
|
40
|
+
# The helper files (for example the file part/users.rb) typically
|
41
|
+
# require the part files needed by default.
|
42
|
+
#
|
43
|
+
# The 'example' application start files (for example part/users/run.rb)
|
44
|
+
# are optional. If present, they start a small application that
|
45
|
+
# demonstrates the usage of the part. In the example app, the main
|
46
|
+
# part controller is mounted at the root ('/'). Typically, in
|
47
|
+
# your own applications, you will mount the controller as needed,
|
48
|
+
# (for example: 'users' => UsersController,
|
49
|
+
# 'blog' => 'ContentController')
|
50
|
+
#
|
51
|
+
# The files that reside in the public directory are typically
|
52
|
+
# copied by a code generator to your application public dir.
|
53
|
+
#
|
54
|
+
# Part controllers setup the template root stack to lookup
|
55
|
+
# templates in their local template dir (for example part/users/template)
|
56
|
+
# if a template is not found in the applications normal template
|
57
|
+
# root. In essence, by requiring a part a target application,
|
58
|
+
# 'inherits' its templates. If you want to customize (override)
|
59
|
+
# one template, just place a template with the same name in the
|
60
|
+
# respective directory in the application template root.
|
61
|
+
|
7
62
|
class Part
|
8
63
|
|
9
64
|
# Require (include) a part in the current application.
|
10
65
|
|
11
|
-
def self.require
|
66
|
+
def self.require name
|
12
67
|
Logger.debug "Requiring part '#{name}'." if $DBG
|
13
68
|
Kernel.require 'part/' + name + '/run.rb'
|
14
69
|
end
|
data/lib/nitro/render.rb
CHANGED
@@ -4,7 +4,6 @@ require 'stringio'
|
|
4
4
|
|
5
5
|
require 'facet/string/blank'
|
6
6
|
|
7
|
-
require 'glue/attribute'
|
8
7
|
require 'glue/settings'
|
9
8
|
require 'glue/template'
|
10
9
|
require 'glue/builder/xml'
|
@@ -16,24 +15,32 @@ require 'nitro/helper/buffer'
|
|
16
15
|
|
17
16
|
module Nitro
|
18
17
|
|
18
|
+
#--
|
19
19
|
# Raise or Throw this exception to stop the current action.
|
20
|
-
# Typically called to skip the template.
|
20
|
+
# Typically called to skip the template. This is considerered
|
21
|
+
# a low level tactic. Prefer to use the exit method.
|
22
|
+
#++
|
21
23
|
|
22
|
-
class ActionExit < Exception
|
24
|
+
class ActionExit < Exception # :nodoc: all
|
25
|
+
end
|
23
26
|
|
27
|
+
#--
|
24
28
|
# Raise or Throw this exception to stop rendering altogether.
|
25
29
|
# Typically called by redirects.
|
30
|
+
#++
|
26
31
|
|
27
|
-
class RenderExit < Exception
|
32
|
+
class RenderExit < Exception # :nodoc: all
|
33
|
+
end
|
28
34
|
|
35
|
+
#--
|
29
36
|
# The output buffer. The output of a contoller action is
|
30
37
|
# accumulated in this buffer before sending this to the client
|
31
38
|
# as a HTTP Response.
|
32
|
-
|
39
|
+
#
|
33
40
|
# TODO: Implement a FAST string (maybe in C)
|
34
41
|
#++
|
35
42
|
|
36
|
-
class OutputBuffer < String
|
43
|
+
class OutputBuffer < String # :nodoc: all
|
37
44
|
end
|
38
45
|
|
39
46
|
# The rendering mixin. This module is typically included in
|
@@ -81,12 +88,9 @@ module Render
|
|
81
88
|
|
82
89
|
attr_accessor :base
|
83
90
|
|
84
|
-
# The
|
85
|
-
#--
|
86
|
-
# gmosx: Needed for WEE. Will be deprecated.
|
87
|
-
#++
|
91
|
+
# The current controller class.
|
88
92
|
|
89
|
-
attr_accessor :
|
93
|
+
attr_accessor :controller
|
90
94
|
|
91
95
|
# Initialize the render.
|
92
96
|
#
|
@@ -94,9 +98,8 @@ module Render
|
|
94
98
|
# A parent render/controller acts as the context.
|
95
99
|
|
96
100
|
def initialize(context, base = nil)
|
97
|
-
@rendering_context = 0
|
98
101
|
@request = @response = @context = context
|
99
|
-
@
|
102
|
+
@base = base
|
100
103
|
@out = context.out
|
101
104
|
end
|
102
105
|
|
@@ -114,21 +117,23 @@ module Render
|
|
114
117
|
|
115
118
|
Logger.debug "Rendering '#{path}'." if $DBG
|
116
119
|
|
117
|
-
|
120
|
+
@controller, action, base = @context.dispatcher.dispatch(path, @context)
|
118
121
|
|
119
|
-
|
120
|
-
@context.content_type = klass.instance_variable_get('@content_type') || 'text/html'
|
122
|
+
raise 'No controller for action' unless @controller
|
121
123
|
|
122
|
-
|
124
|
+
# FIXME:
|
125
|
+
@context.content_type = @controller.instance_variable_get('@content_type') || 'text/html'
|
123
126
|
|
124
127
|
@context.level += 1
|
125
|
-
|
126
|
-
|
128
|
+
old_controller = Controller.replace_current(@controller)
|
129
|
+
|
130
|
+
if self.class == @controller
|
127
131
|
self.send(action)
|
128
132
|
else
|
129
|
-
|
133
|
+
@controller.new(@context, base).send(action)
|
130
134
|
end
|
131
135
|
|
136
|
+
Controller.replace_current(old_controller)
|
132
137
|
@context.level -= 1
|
133
138
|
|
134
139
|
rescue NoActionError => e1
|
@@ -142,6 +147,7 @@ module Render
|
|
142
147
|
rescue Exception, StandardError => e3
|
143
148
|
# More fault tolerant, only flags the erroneous box with
|
144
149
|
# error not the full page.
|
150
|
+
|
145
151
|
log_error(e3, path)
|
146
152
|
print '(error)'
|
147
153
|
end
|
@@ -149,13 +155,21 @@ module Render
|
|
149
155
|
private
|
150
156
|
|
151
157
|
# Helper method to exit the current action, typically used
|
152
|
-
# to skip the template.
|
158
|
+
# to skip the template rendering.
|
159
|
+
#
|
160
|
+
# === Example
|
161
|
+
#
|
162
|
+
# def my_action
|
163
|
+
# ...
|
164
|
+
# exit unless user.admin?
|
165
|
+
# end
|
153
166
|
|
154
167
|
def exit
|
155
168
|
raise ActionExit.new
|
156
169
|
end
|
157
170
|
|
158
|
-
# Flush the IO object if we are in streaming
|
171
|
+
# Flush the IO object (OutputBuffer) if we are in streaming
|
172
|
+
# mode.
|
159
173
|
|
160
174
|
def flush
|
161
175
|
@out.flush if @out.is_a?(IO)
|
@@ -168,9 +182,34 @@ private
|
|
168
182
|
# If the url starts with '/' it is considered absolute, else
|
169
183
|
# the url is considered relative to the current controller and
|
170
184
|
# the controller base is prepended.
|
185
|
+
#
|
186
|
+
# The parameters are passed to the R operator (encode_url)
|
187
|
+
# to actually build the url. So the following forms (among
|
188
|
+
# others) are allowed:
|
189
|
+
#
|
190
|
+
# redirect 'home/login'
|
191
|
+
# redirect ForaController, :post, :title, 'The title'
|
192
|
+
# redirect :welcome
|
193
|
+
# redirect article # => article.to_href
|
194
|
+
#
|
195
|
+
# You can also pass optional hash parameters at the end,
|
196
|
+
# for example:
|
197
|
+
#
|
198
|
+
# redirect :welcome, :status => 307
|
199
|
+
#
|
200
|
+
# The default redirect status is 303.
|
171
201
|
|
172
|
-
def redirect(
|
173
|
-
|
202
|
+
def redirect(*args)
|
203
|
+
if args.last.is_a? Hash
|
204
|
+
status = args.last.fetch(:status, 303)
|
205
|
+
else
|
206
|
+
status = 303
|
207
|
+
end
|
208
|
+
|
209
|
+
url = encode_url(*args)
|
210
|
+
|
211
|
+
# gmosx, THINK: this may be unnecessary!
|
212
|
+
|
174
213
|
unless url =~ /^http/
|
175
214
|
url = "#@base/#{url}" unless url =~ /^\//
|
176
215
|
url = "#{@context.host_url}/#{url.gsub(/^\//, '')}"
|
@@ -187,7 +226,7 @@ private
|
|
187
226
|
# Redirect to the referer.
|
188
227
|
|
189
228
|
def redirect_referer(postfix = nil, status = 303)
|
190
|
-
redirect
|
229
|
+
redirect "#{@context.referer}#{postfix}", :status => status
|
191
230
|
end
|
192
231
|
alias_method :redirect_to_referer, :redirect_referer
|
193
232
|
alias_method :redirect_referrer, :redirect_referer
|
@@ -196,7 +235,7 @@ private
|
|
196
235
|
# Redirect to home.
|
197
236
|
|
198
237
|
def redirect_home(status = 303)
|
199
|
-
redirect
|
238
|
+
redirect '/', :status => status
|
200
239
|
end
|
201
240
|
alias_method :redirect_to_home, :redirect_home
|
202
241
|
|
@@ -209,10 +248,10 @@ private
|
|
209
248
|
# === Example
|
210
249
|
#
|
211
250
|
# caller:
|
212
|
-
# color, type = call
|
251
|
+
# color, type = call 'utils/select_color'
|
213
252
|
#
|
214
253
|
# target:
|
215
|
-
# answer
|
254
|
+
# answer color, type
|
216
255
|
#--
|
217
256
|
# FIXME: dont use yet, you have to encode the branch to
|
218
257
|
# make this safe for use.
|