nitro 0.21.2 → 0.22.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +121 -0
- data/README +9 -3
- data/doc/RELEASES +86 -1
- data/lib/nitro.rb +1 -1
- data/lib/nitro/adapter/cgi.rb +4 -1
- data/lib/nitro/adapter/scgi.rb +2 -0
- data/lib/nitro/adapter/webrick.rb +1 -1
- data/lib/nitro/caching/output.rb +6 -5
- data/lib/nitro/compiler.rb +8 -3
- data/lib/nitro/dispatcher.rb +2 -2
- data/lib/nitro/dispatcher/nice.rb +1 -1
- data/lib/nitro/element.rb +19 -2
- data/lib/nitro/mixin/markup.rb +2 -2
- data/lib/nitro/mixin/pager.rb +80 -25
- data/lib/nitro/mixin/rss.rb +5 -2
- data/lib/nitro/render.rb +9 -0
- data/lib/nitro/request.rb +93 -6
- data/lib/nitro/response.rb +4 -0
- data/lib/nitro/server.rb +24 -10
- data/lib/nitro/server/runner.rb +15 -10
- data/lib/nitro/service/xmlrpc.rb +1 -0
- data/lib/nitro/test.rb +5 -0
- data/lib/nitro/test/assertions.rb +171 -0
- data/lib/nitro/{testing → test}/context.rb +0 -0
- data/lib/nitro/test/testcase.rb +66 -0
- data/proto/public/error.xhtml +5 -2
- data/proto/public/settings.xhtml +2 -0
- data/proto/script/runner +20 -0
- data/test/nitro/tc_controller.rb +3 -3
- data/test/nitro/tc_controller_aspect.rb +29 -0
- data/test/nitro/tc_dispatcher.rb +2 -1
- data/test/nitro/tc_element.rb +9 -0
- data/test/nitro/tc_request.rb +38 -0
- metadata +13 -13
- data/lib/nitro/mail.rb +0 -270
- data/lib/nitro/template.rb +0 -202
- data/lib/nitro/testing.rb +0 -2
- data/lib/nitro/testing/assertions.rb +0 -100
- data/lib/nitro/testing/testcase.rb +0 -51
- data/test/nitro/tc_mail.rb +0 -97
- data/test/nitro/tc_template.rb +0 -32
data/lib/nitro/mixin/markup.rb
CHANGED
@@ -21,7 +21,7 @@ module Nitro
|
|
21
21
|
#
|
22
22
|
# Define your custom markup methods like this:
|
23
23
|
#
|
24
|
-
# module
|
24
|
+
# module Markup
|
25
25
|
# def markup_simple
|
26
26
|
# ...
|
27
27
|
# end
|
@@ -49,7 +49,7 @@ private
|
|
49
49
|
def expand(str)
|
50
50
|
if str
|
51
51
|
xstr = str.dup
|
52
|
-
|
52
|
+
# xstr.gsub!(/</, '<')
|
53
53
|
xstr.gsub!(/>/, '>')
|
54
54
|
return String.sanitize(xstr)
|
55
55
|
end
|
data/lib/nitro/mixin/pager.rb
CHANGED
@@ -8,9 +8,9 @@ module Nitro
|
|
8
8
|
# === Design
|
9
9
|
#
|
10
10
|
# This pager is carefully designed for scaleability. It stores
|
11
|
-
# only the items for one page. The
|
12
|
-
# pagers can coexist in a single page.
|
13
|
-
#
|
11
|
+
# only the items for one page. The key parameter is needed,
|
12
|
+
# multiple pagers can coexist in a single page. The pager
|
13
|
+
# leverages the SQL LIMIT option to optimize database
|
14
14
|
# interaction.
|
15
15
|
|
16
16
|
class Pager
|
@@ -53,21 +53,53 @@ class Pager
|
|
53
53
|
@page_count = (@total_count.to_f / @per_page).ceil
|
54
54
|
end
|
55
55
|
|
56
|
+
# Return the first page index.
|
57
|
+
|
56
58
|
def first_page
|
57
59
|
return 1
|
58
60
|
end
|
59
61
|
|
62
|
+
# Is the first page displayed?
|
63
|
+
|
64
|
+
def first_page?
|
65
|
+
@page == 1
|
66
|
+
end
|
67
|
+
|
68
|
+
# Return the last page index.
|
69
|
+
|
60
70
|
def last_page
|
61
71
|
return @page_count
|
62
72
|
end
|
63
73
|
|
74
|
+
# Is the last page displayed?
|
75
|
+
|
76
|
+
def last_page?
|
77
|
+
@page == @page_count
|
78
|
+
end
|
79
|
+
|
80
|
+
# Return the index of the previous page.
|
81
|
+
|
64
82
|
def previous_page
|
65
83
|
return [@page - 1, 1].max()
|
66
84
|
end
|
85
|
+
|
86
|
+
# Return the index of the next page.
|
67
87
|
|
68
88
|
def next_page
|
69
89
|
return [@page + 1, @page_count].min()
|
70
90
|
end
|
91
|
+
|
92
|
+
# A set of helpers to create links to common pages.
|
93
|
+
|
94
|
+
for target in [:first, :last, :previous, :next]
|
95
|
+
eval %{
|
96
|
+
def link_#{target}_page
|
97
|
+
target_uri(#{target}_page)
|
98
|
+
end
|
99
|
+
alias_method :#{target}_page_uri, :link_#{target}_page
|
100
|
+
alias_method :#{target}_page_href, :link_#{target}_page
|
101
|
+
}
|
102
|
+
end
|
71
103
|
|
72
104
|
# Iterator
|
73
105
|
|
@@ -119,7 +151,11 @@ class Pager
|
|
119
151
|
# To be used with Og queries.
|
120
152
|
|
121
153
|
def limit
|
122
|
-
|
154
|
+
if @start_idx > 0
|
155
|
+
{ :limit => @per_page, :offset => @start_idx }
|
156
|
+
else
|
157
|
+
{ :limit => @per_page }
|
158
|
+
end
|
123
159
|
end
|
124
160
|
|
125
161
|
def offset
|
@@ -146,17 +182,17 @@ class Pager
|
|
146
182
|
def navigation
|
147
183
|
nav = ""
|
148
184
|
|
149
|
-
unless
|
185
|
+
unless first_page?
|
150
186
|
nav << %{
|
151
|
-
<div class="first"><a href="#{
|
152
|
-
<div class="previous"><a href="#{
|
187
|
+
<div class="first"><a href="#{first_page_href}">First</a></div>
|
188
|
+
<div class="previous"><a href="#{previous_page_href}">Previous</a></div>
|
153
189
|
}
|
154
190
|
end
|
155
191
|
|
156
|
-
unless
|
192
|
+
unless last_page?
|
157
193
|
nav << %{
|
158
|
-
<div class="last"><a href="#{
|
159
|
-
<div class="next"><a href="#{
|
194
|
+
<div class="last"><a href="#{last_page_href}">Last</a></div>
|
195
|
+
<div class="next"><a href="#{next_page_href}">Next</a></div>
|
160
196
|
}
|
161
197
|
end
|
162
198
|
|
@@ -198,32 +234,51 @@ module PagerMixin
|
|
198
234
|
private
|
199
235
|
|
200
236
|
# Helper method that generates a collection of items and the
|
201
|
-
#
|
237
|
+
# associated pager object.
|
202
238
|
#
|
203
239
|
# === Example
|
204
240
|
#
|
205
241
|
# entries, pager = paginate(Article, :condition => 'title LIKE %Ab%', :per_page => 10)
|
206
242
|
#
|
243
|
+
# or
|
244
|
+
#
|
245
|
+
# items = [ 'item1', 'item2', ... ]
|
246
|
+
# entries, pager = paginate(items, :per_page => 10)
|
247
|
+
#
|
248
|
+
# or
|
249
|
+
#
|
250
|
+
# entries, pager = paginate(article.comments, :per_page => 10)
|
251
|
+
#
|
207
252
|
# <ul>
|
208
253
|
# <?r for entry in entries ?>
|
209
254
|
# <li>#{entry.to_link}</li>
|
210
255
|
# <?r end ?>
|
211
|
-
#
|
212
|
-
#
|
256
|
+
# </ul>
|
257
|
+
# #{pager.links}
|
213
258
|
|
214
|
-
def paginate(
|
259
|
+
def paginate(items, options = {})
|
215
260
|
per_page = options.delete(:per_page) || Pager.per_page
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
261
|
+
|
262
|
+
case items
|
263
|
+
when Array
|
264
|
+
items = items.dup
|
265
|
+
pager = Pager.new(request, per_page, items.size, key = Pager.key)
|
266
|
+
items = items.slice(pager.offset, pager.per_page)
|
267
|
+
return items, pager
|
268
|
+
|
269
|
+
when Og::Collection
|
270
|
+
collection = items
|
271
|
+
pager = Pager.new(request, per_page, collection.count, key = Pager.key)
|
272
|
+
options.update(pager.limit)
|
273
|
+
items = collection.reload(options)
|
274
|
+
return items, pager
|
275
|
+
|
276
|
+
when Class
|
277
|
+
klass = items
|
278
|
+
pager = Pager.new(request, per_page, klass.count(options), key = Pager.key)
|
279
|
+
options.update(pager.limit)
|
280
|
+
items = klass.all(options)
|
281
|
+
return items, pager
|
227
282
|
end
|
228
283
|
end
|
229
284
|
|
data/lib/nitro/mixin/rss.rb
CHANGED
@@ -3,6 +3,8 @@ require 'rss/0.9'
|
|
3
3
|
|
4
4
|
require 'facet/string/first_char'
|
5
5
|
|
6
|
+
require 'nitro/mixin/markup'
|
7
|
+
|
6
8
|
module Nitro
|
7
9
|
|
8
10
|
# Build RSS represenations of ruby object collections.
|
@@ -17,7 +19,8 @@ module Nitro
|
|
17
19
|
#++
|
18
20
|
|
19
21
|
module RssMixin
|
20
|
-
|
22
|
+
include Nitro::Markup
|
23
|
+
|
21
24
|
# === Options
|
22
25
|
#
|
23
26
|
# [+:description+]
|
@@ -44,7 +47,7 @@ module RssMixin
|
|
44
47
|
item = RSS::Rss::Channel::Item.new
|
45
48
|
item.title = obj.title if obj.respond_to?(:title)
|
46
49
|
if obj.respond_to? :body and body = obj.body
|
47
|
-
item.description =
|
50
|
+
item.description = markup(body.first_char(256))
|
48
51
|
end
|
49
52
|
if obj.respond_to? :to_href
|
50
53
|
item.link = "#{c[:base]}/#{obj.to_href}"
|
data/lib/nitro/render.rb
CHANGED
@@ -7,6 +7,7 @@ require 'glue/attribute'
|
|
7
7
|
require 'glue/misc'
|
8
8
|
require 'glue/object'
|
9
9
|
require 'glue/settings'
|
10
|
+
require 'glue/template'
|
10
11
|
require 'glue/builder'
|
11
12
|
require 'glue/builder/xml'
|
12
13
|
|
@@ -173,6 +174,14 @@ private
|
|
173
174
|
end
|
174
175
|
alias_method :print, :render_text
|
175
176
|
|
177
|
+
# Render a template into the output buffer.
|
178
|
+
|
179
|
+
def render_template(filename)
|
180
|
+
filename = "#{filename}.xhtml" unless filename =~ /\.xhtml$/
|
181
|
+
template = File.read("#{self.class.template_root}/#{filename}")
|
182
|
+
Template.process_template(template, '@out', binding)
|
183
|
+
end
|
184
|
+
|
176
185
|
# Access the programmatic renderer (builder).
|
177
186
|
|
178
187
|
def build(&block)
|
data/lib/nitro/request.rb
CHANGED
@@ -2,7 +2,7 @@ module Nitro
|
|
2
2
|
|
3
3
|
# Encapsulates a request. This is an abstract request
|
4
4
|
# typically extended by sub-classes. This module
|
5
|
-
# is included in Context
|
5
|
+
# is included in Context.
|
6
6
|
|
7
7
|
module Request
|
8
8
|
|
@@ -16,7 +16,6 @@ module Request
|
|
16
16
|
attr_accessor :headers
|
17
17
|
alias_method :env, :headers
|
18
18
|
alias_method :env=, :headers=
|
19
|
-
# for compatibility with cgi.rb
|
20
19
|
alias_method :env_table, :headers
|
21
20
|
|
22
21
|
# The parsed query parameters collection.
|
@@ -56,23 +55,107 @@ module Request
|
|
56
55
|
@headers['PATH_INFO']
|
57
56
|
end
|
58
57
|
|
58
|
+
# Returns the domain part of a host.
|
59
|
+
#
|
60
|
+
# === Examples
|
61
|
+
#
|
62
|
+
# www.nitrohq.com: request.domain # => 'nitrohq.com'
|
63
|
+
# www.nitrohq.co.uk: request.domain(2) # => 'nitrohq.co.uk'
|
64
|
+
|
65
|
+
def domain(tld_length = 1)
|
66
|
+
host.split('.').last(1 + tld_length).join('.')
|
67
|
+
end
|
68
|
+
|
69
|
+
# Returns all the subdomains as an array.
|
70
|
+
#
|
71
|
+
# === Examples
|
72
|
+
#
|
73
|
+
# my.name.nitrohq.com: request.subdomains # => ['my', 'name']
|
74
|
+
|
75
|
+
def subdomains(tld_length = 1)
|
76
|
+
parts = host.split('.')
|
77
|
+
parts[0..-(tld_length+2)]
|
78
|
+
end
|
79
|
+
|
59
80
|
# The request query string.
|
60
81
|
|
61
82
|
def query_string
|
62
83
|
@headers['QUERY_STRING']
|
63
84
|
end
|
64
85
|
|
65
|
-
# The request method.
|
86
|
+
# The request method. Alternatively you could use the
|
87
|
+
# request method predicates.
|
88
|
+
#
|
89
|
+
# === Examples
|
90
|
+
#
|
91
|
+
# if request.method == :get
|
92
|
+
# if request.get?
|
66
93
|
|
67
94
|
def method
|
68
95
|
@headers['REQUEST_METHOD'].downcase.intern
|
69
96
|
end
|
70
97
|
|
71
|
-
|
98
|
+
#--
|
99
|
+
# Define a set of helpers to determine the request
|
100
|
+
# method (get?, post?, put?, delete?, head?)
|
101
|
+
#++
|
102
|
+
|
103
|
+
for m in [:get, :post, :put, :delete, :head]
|
104
|
+
eval %{
|
105
|
+
def #{m}?; method == :#{m}; end
|
106
|
+
}
|
107
|
+
end
|
108
|
+
|
109
|
+
# Determine whether the body of a POST request is URL-encoded
|
110
|
+
# (default), XML, or YAML by checking the Content-Type HTTP
|
111
|
+
# header:
|
112
|
+
#
|
113
|
+
# Content-Type Post Format
|
114
|
+
# application/xml :xml
|
115
|
+
# text/xml :xml
|
116
|
+
# application/x-yaml :yaml
|
117
|
+
# text/x-yaml :yaml
|
118
|
+
# * :url_encoded
|
72
119
|
|
73
|
-
def
|
74
|
-
|
120
|
+
def post_format
|
121
|
+
@post_format ||= if @headers['HTTP_X_POST_DATA_FORMAT']
|
122
|
+
@headers['HTTP_X_POST_DATA_FORMAT'].downcase.to_sym
|
123
|
+
else
|
124
|
+
case @headers['CONTENT_TYPE'].to_s.downcase
|
125
|
+
when 'application/xml', 'text/xml' then :xml
|
126
|
+
when 'application/x-yaml', 'text/x-yaml' then :yaml
|
127
|
+
else :url_encoded
|
128
|
+
end
|
129
|
+
end
|
75
130
|
end
|
131
|
+
|
132
|
+
# Is this a POST request formatted as XML or YAML?
|
133
|
+
|
134
|
+
def formatted_post?
|
135
|
+
post? && (post_format == :xml || post_format == :yaml)
|
136
|
+
end
|
137
|
+
|
138
|
+
# Is this a POST request formatted as XML?
|
139
|
+
|
140
|
+
def xml_post?
|
141
|
+
post? && post_format == :xml
|
142
|
+
end
|
143
|
+
|
144
|
+
# Is this a POST request formatted as YAML?
|
145
|
+
|
146
|
+
def yaml_post?
|
147
|
+
post? && post_format == :yaml
|
148
|
+
end
|
149
|
+
|
150
|
+
# Is this an XhtmlRpcRequest?
|
151
|
+
# Returns true if the request's 'X-Requested-With' header
|
152
|
+
# contains 'XMLHttpRequest'. Compatible with the Prototype
|
153
|
+
# Javascript library.
|
154
|
+
|
155
|
+
def xml_http_request?
|
156
|
+
not /XMLHttpRequest/i.match(@headers['HTTP_X_REQUESTED_WITH']).nil?
|
157
|
+
end
|
158
|
+
alias xhr? :xml_http_request?
|
76
159
|
|
77
160
|
# Return the referer. For the initial page in the
|
78
161
|
# clickstream there is no referer, set "/" by default.
|
@@ -122,6 +205,8 @@ module Request
|
|
122
205
|
@headers['HTTP_X_FORWARDED_HOST'] || @headers['HTTP_HOST']
|
123
206
|
end
|
124
207
|
|
208
|
+
# The host url.
|
209
|
+
|
125
210
|
def host_url
|
126
211
|
"http://#{host}"
|
127
212
|
end
|
@@ -161,3 +246,5 @@ module Request
|
|
161
246
|
end
|
162
247
|
|
163
248
|
end
|
249
|
+
|
250
|
+
# * George Moschovitis <gm@navel.gr>
|
data/lib/nitro/response.rb
CHANGED
data/lib/nitro/server.rb
CHANGED
@@ -61,7 +61,7 @@ class Server
|
|
61
61
|
# The access_log for this server, used by Webrick.
|
62
62
|
|
63
63
|
attr_accessor :access_log
|
64
|
-
|
64
|
+
|
65
65
|
def initialize(name = 'Nitro', options = {})
|
66
66
|
@name = name
|
67
67
|
@map = self.class.map.dup
|
@@ -82,9 +82,9 @@ class Server
|
|
82
82
|
|
83
83
|
# Start the server.
|
84
84
|
|
85
|
-
def start(
|
86
|
-
@map['/'] = controller if controller
|
87
|
-
@dispatcher = Dispatcher.new(@map)
|
85
|
+
def start(options = {})
|
86
|
+
@map['/'] = options[:controller] if options[:controller]
|
87
|
+
@dispatcher = options[:dispatche] || Dispatcher.new(@map)
|
88
88
|
end
|
89
89
|
|
90
90
|
def root=(controller)
|
@@ -96,15 +96,30 @@ class Server
|
|
96
96
|
end
|
97
97
|
|
98
98
|
# Helper method.
|
99
|
-
|
100
|
-
|
99
|
+
#
|
100
|
+
# Available options:
|
101
|
+
#
|
102
|
+
# :dispatcher, :controller
|
103
|
+
#
|
104
|
+
# Altetnatively you can pass a single Controller class instead
|
105
|
+
# of the options hash.
|
106
|
+
|
107
|
+
def self.run(options = {})
|
108
|
+
unless options.is_a?(Hash)
|
109
|
+
options = { :controller => options }
|
110
|
+
end
|
111
|
+
|
101
112
|
runner = Runner.new
|
102
113
|
runner.setup_options
|
103
114
|
runner.setup_mode
|
104
115
|
runner.daemonize if runner.daemon
|
116
|
+
|
105
117
|
server = Server.new
|
106
|
-
server.start(
|
107
|
-
|
118
|
+
server.start(options)
|
119
|
+
|
120
|
+
runner.invoke(server) unless $NITRO_NO_INVOKE
|
121
|
+
|
122
|
+
return server
|
108
123
|
end
|
109
124
|
|
110
125
|
# A Helper class used for CherryPy-style publishing.
|
@@ -123,8 +138,7 @@ class Server
|
|
123
138
|
end
|
124
139
|
end
|
125
140
|
end
|
126
|
-
|
127
|
-
|
141
|
+
|
128
142
|
end
|
129
143
|
|
130
144
|
end
|