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/ProjectInfo
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
TITLE : &title Nitro
|
4
4
|
NAME : &pkg nitro
|
5
|
-
VERSION : '0.
|
5
|
+
VERSION : '0.31.0'
|
6
6
|
STATUS : beta
|
7
7
|
|
8
8
|
AUTHOR : George Moschovitis
|
@@ -26,12 +26,12 @@ RUBYFORGE:
|
|
26
26
|
USERNAME: 'gmosx'
|
27
27
|
|
28
28
|
DEPENDENCIES:
|
29
|
-
- [ og, '= 0.
|
30
|
-
- [ gen, '= 0.
|
31
|
-
- [ glue, '= 0.
|
29
|
+
- [ og, '= 0.31.0' ]
|
30
|
+
- [ gen, '= 0.31.0' ]
|
31
|
+
- [ glue, '= 0.31.0' ]
|
32
32
|
- [ RedCloth, '= 3.0.3' ]
|
33
|
-
- [ ruby-breakpoint, '
|
34
|
-
- [ daemons, '
|
33
|
+
- [ ruby-breakpoint, '~> 0.5' ]
|
34
|
+
- [ daemons, '~> 0.4' ]
|
35
35
|
|
36
36
|
EXECUTABLES:
|
37
37
|
- nitro
|
data/bin/nitro
CHANGED
@@ -1,12 +1,105 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
-
|
3
|
+
require 'facets/more/consoleapp'
|
4
4
|
|
5
|
-
|
5
|
+
class NitroCommand < Console::Command
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
7
|
+
# :section: Commands
|
8
|
+
|
9
|
+
# The default action, starts the application. You can
|
10
|
+
# alternatively use the start/run aliases.
|
11
|
+
#
|
12
|
+
# === Examples
|
13
|
+
#
|
14
|
+
# $ nitro
|
15
|
+
# $ nitro start
|
16
|
+
# $ nitro run
|
17
|
+
|
18
|
+
def default
|
19
|
+
if f = application_file
|
20
|
+
$NITRO_NO_ENVIRONMENT = true
|
21
|
+
load 'run.rb'
|
22
|
+
else
|
23
|
+
puts 'No application found!'
|
24
|
+
# FIXME: better error mesage and/or show default app!
|
25
|
+
end
|
26
|
+
end
|
27
|
+
alias run default
|
28
|
+
alias start default
|
29
|
+
|
30
|
+
# Starts an IRB console attached to the web application.
|
31
|
+
|
32
|
+
def console
|
33
|
+
if RUBY_PLATFORM =~ /mswin32/
|
34
|
+
irb_name = 'irb.bat'
|
35
|
+
else
|
36
|
+
irb_name = 'irb'
|
37
|
+
end
|
38
|
+
|
39
|
+
ENV['NITRO_INVOKE'] = 'irb'
|
40
|
+
|
41
|
+
if f = application_file
|
42
|
+
ENV['NITRO_MODE'] = $DBG ? 'debug' : 'live'
|
43
|
+
exec "#{irb_name} -r #{f} -r irb/completion --noinspect"
|
44
|
+
end
|
45
|
+
|
46
|
+
exit
|
47
|
+
end
|
48
|
+
|
49
|
+
# Dump the version of Nitro.
|
50
|
+
|
51
|
+
def version
|
52
|
+
puts "Nitro 0.31.0"
|
53
|
+
end
|
54
|
+
|
55
|
+
# :section: Options
|
56
|
+
|
57
|
+
# Enable verbose mode.
|
58
|
+
|
59
|
+
def _v
|
60
|
+
$DBG = true
|
61
|
+
ENV['NITRO_MODE'] = 'debug'
|
62
|
+
end
|
63
|
+
alias _verbose _v
|
64
|
+
|
65
|
+
# Daemonize the server. Typically used with the Webrick
|
66
|
+
# adapter.
|
67
|
+
|
68
|
+
def _daemon
|
69
|
+
require 'daemons/daemonize'
|
70
|
+
|
71
|
+
pwd = Dir.pwd
|
72
|
+
Daemonize.daemonize(File.join(pwd, 'log/app.log'))
|
73
|
+
|
74
|
+
# Restore the original pwd (daemonize sets the
|
75
|
+
# pwd to '/').
|
76
|
+
Dir.chdir(pwd)
|
77
|
+
|
78
|
+
# Set the logger to a file (daemonize closes the
|
79
|
+
# std streams).
|
80
|
+
Logger.set(Logger.new('log/app.log'))
|
81
|
+
end
|
82
|
+
alias _daemonize _daemon
|
83
|
+
|
84
|
+
private
|
85
|
+
|
86
|
+
# Typical application main file names.
|
87
|
+
|
88
|
+
APPLICATION_FILES = %w{ run.rb start.rb conf.rb app.rb application.rb }
|
89
|
+
|
90
|
+
# Find out the application main file.
|
91
|
+
|
92
|
+
def application_file
|
93
|
+
for f in APPLICATION_FILES
|
94
|
+
if File.exist? f
|
95
|
+
return f
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
return false
|
100
|
+
end
|
12
101
|
end
|
102
|
+
|
103
|
+
NitroCommand.new.execute
|
104
|
+
|
105
|
+
# * George Moschovitis <gm@navel.gr>
|
data/doc/RELEASES
CHANGED
@@ -1,3 +1,21 @@
|
|
1
|
+
== Version 0.31.0
|
2
|
+
|
3
|
+
* Mongrel adapter updated to work with latest versions of Mongrel.
|
4
|
+
|
5
|
+
* Fixed a long-standing bug where the template root wasn't determined correctly (would result in blank pages).
|
6
|
+
|
7
|
+
* Added sendfile support.
|
8
|
+
|
9
|
+
* Fixed strip_path support. When this setting is set to a path, it will be stripped from urls. Given,
|
10
|
+
|
11
|
+
Router.strip_path = '/nitro-apps'
|
12
|
+
|
13
|
+
when the dispatcher gets a url like /nitro-apps/blog it will strip out /nitro-apps and resolve to the controller for /blog.
|
14
|
+
|
15
|
+
* Wee helper is removed.
|
16
|
+
|
17
|
+
* Updated Nitro start page with links to the examples.
|
18
|
+
|
1
19
|
== Version 0.30.0
|
2
20
|
|
3
21
|
Another pragmatic release. The Nitro development team worked over
|
data/lib/glue/sweeper.rb
CHANGED
@@ -12,22 +12,26 @@ module Glue
|
|
12
12
|
#++
|
13
13
|
|
14
14
|
module Sweeper
|
15
|
-
include
|
15
|
+
include Aspects
|
16
16
|
|
17
17
|
before "sweep_affected(:insert)", :on => :og_insert
|
18
18
|
before "sweep_affected(:update)", :on => :og_update
|
19
19
|
before "sweep_affected(:delete)", :on => :og_delete
|
20
20
|
|
21
|
+
# Expires (deletes) a cached page from the file system.
|
21
22
|
#--
|
22
23
|
# FIXME: replace with 'extend Nitro::Caching::Output' ?
|
24
|
+
# this way we wont have to sync the same code in two different
|
25
|
+
# places.
|
23
26
|
# If you change this method, don't forget the Caching::Output
|
24
27
|
# expire method.
|
25
28
|
#++
|
26
29
|
|
27
|
-
def self.expire(name)
|
30
|
+
def self.expire(name, klass)
|
28
31
|
begin
|
29
|
-
|
30
|
-
|
32
|
+
filename = "#{Server.public_root}/#{klass.ann.self.controller.mount_path}/#{name}".squeeze('/')
|
33
|
+
Logger.debug "Sweeper expired cache file '#{filename}'" if $DBG
|
34
|
+
FileUtils.rm_rf(filename)
|
31
35
|
rescue Object
|
32
36
|
# gmosx: is this the right thing to do?
|
33
37
|
end
|
@@ -53,7 +57,7 @@ private
|
|
53
57
|
#++
|
54
58
|
|
55
59
|
def expire_affected_output(name)
|
56
|
-
Sweeper.expire(name)
|
60
|
+
Sweeper.expire(name, self.class)
|
57
61
|
end
|
58
62
|
alias_method :expire_output, :expire_affected_output
|
59
63
|
|
data/lib/nitro.rb
CHANGED
@@ -16,7 +16,7 @@ module Nitro
|
|
16
16
|
|
17
17
|
# The version.
|
18
18
|
|
19
|
-
Version = '0.
|
19
|
+
Version = '0.31.0'
|
20
20
|
|
21
21
|
# Library path.
|
22
22
|
|
@@ -35,18 +35,6 @@ module Nitro
|
|
35
35
|
include Glue
|
36
36
|
end
|
37
37
|
|
38
|
-
#--
|
39
|
-
# gmosx: leave them here.
|
40
|
-
#++
|
41
|
-
|
42
|
-
require 'nitro/global'
|
43
|
-
require 'nitro/context'
|
44
|
-
require 'nitro/controller'
|
45
|
-
require 'nitro/dispatcher'
|
46
|
-
require 'nitro/render'
|
47
|
-
require 'nitro/server'
|
48
|
-
require 'nitro/part'
|
49
|
-
|
50
38
|
unless $NITRO_NO_ENVIRONMENT
|
51
39
|
# Setup up the proposed environment. You are free
|
52
40
|
# to skip this if you dont like it. Just set
|
@@ -68,6 +56,18 @@ unless $NITRO_NO_ENVIRONMENT
|
|
68
56
|
$LOAD_PATH.unshift 'lib'
|
69
57
|
end
|
70
58
|
|
59
|
+
#--
|
60
|
+
# gmosx: leave them here.
|
61
|
+
#++
|
62
|
+
|
63
|
+
require 'nitro/global'
|
64
|
+
require 'nitro/context'
|
65
|
+
require 'nitro/controller'
|
66
|
+
require 'nitro/dispatcher'
|
67
|
+
require 'nitro/render'
|
68
|
+
require 'nitro/server'
|
69
|
+
require 'nitro/part'
|
70
|
+
|
71
71
|
module Nitro
|
72
72
|
|
73
73
|
class << self
|
@@ -28,8 +28,7 @@ module Mongrel # :nodoc: all
|
|
28
28
|
end
|
29
29
|
end
|
30
30
|
end
|
31
|
-
|
32
|
-
|
31
|
+
|
33
32
|
module Nitro
|
34
33
|
|
35
34
|
class Mongrel
|
@@ -38,66 +37,53 @@ class Mongrel
|
|
38
37
|
attr_accessor :mongrel
|
39
38
|
|
40
39
|
# Start the Webrick adapter.
|
41
|
-
|
40
|
+
|
42
41
|
def start(server)
|
43
42
|
# TODO add logging.
|
44
43
|
|
45
|
-
|
44
|
+
mongrel_options = server.options.dup
|
46
45
|
|
47
|
-
|
46
|
+
mongrel_options.update(
|
48
47
|
:BindAddress => server.address,
|
49
48
|
:Port => server.port,
|
50
49
|
:DocumentRoot => server.public_root
|
51
50
|
)
|
51
|
+
|
52
52
|
@mongrel = ::Mongrel::HttpServer.new(mongrel_options[:BindAddress],
|
53
|
-
|
54
|
-
|
55
|
-
trap('INT') {
|
53
|
+
mongrel_options[:Port])
|
54
|
+
|
55
|
+
trap('INT') { exit } # works until you use breakpoint... why?
|
56
56
|
|
57
57
|
@mongrel.register('/', MongrelAdapter.new(server))
|
58
|
-
|
58
|
+
|
59
59
|
initialize_mongrel(server)
|
60
|
-
|
60
|
+
|
61
61
|
@mongrel.run
|
62
62
|
@mongrel_thread = @mongrel.acceptor
|
63
63
|
@mongrel_thread.join
|
64
64
|
end
|
65
|
-
|
65
|
+
|
66
66
|
# Stop the Mongrel adapter.
|
67
|
-
|
67
|
+
|
68
68
|
def stop
|
69
|
-
|
69
|
+
@mongrel_thread.kill
|
70
70
|
end
|
71
|
-
|
71
|
+
|
72
72
|
# Override this method to perform customized mongrel
|
73
73
|
# initialization.
|
74
74
|
|
75
75
|
def initialize_mongrel(server)
|
76
76
|
end
|
77
|
-
|
77
|
+
|
78
78
|
end
|
79
|
-
|
79
|
+
|
80
80
|
end
|
81
81
|
|
82
|
-
# A special handler for Xhtml files.
|
83
|
-
|
84
|
-
#class XhtmlFileHandler < WEBrick::HTTPServlet::DefaultFileHandler
|
85
|
-
# def do_GET(req, res)
|
86
|
-
# res['content-type'] = 'text/html'
|
87
|
-
# res.body = '<html><body>Permission denied</body></html>'
|
88
|
-
# end
|
89
|
-
#end
|
90
|
-
|
91
82
|
# A Mongrel Adapter for Nitro.
|
92
83
|
|
93
84
|
class MongrelAdapter < ::Mongrel::HttpHandler
|
94
85
|
attr_accessor :server
|
95
|
-
|
96
|
-
STATUS_CODES = {
|
97
|
-
"200" => "OK", "304" => "Not Modified",
|
98
|
-
"404" => "Not found", "500" => "Server Error"
|
99
|
-
}
|
100
|
-
|
86
|
+
|
101
87
|
def initialize(server)
|
102
88
|
@server = server
|
103
89
|
end
|
@@ -112,17 +98,11 @@ class MongrelAdapter < ::Mongrel::HttpHandler
|
|
112
98
|
begin
|
113
99
|
rewrite(req)
|
114
100
|
# TODO handle If-Modified-Since and add Last-Modified headers
|
115
|
-
filename = [@server.public_root, req.path_info].join("/")
|
116
|
-
ext = File.extname(filename)
|
117
|
-
content_type = ::Mongrel::DirHandler::MIME_TYPES[ext] || "text/plain"
|
101
|
+
filename = [@server.public_root, req.path_info].join("/").gsub(%r[//], '/')
|
118
102
|
File.open(filename, "rb") { |f|
|
119
103
|
# TODO check whether path circumvents public_root directory?
|
120
|
-
|
121
|
-
res.
|
122
|
-
res.socket << "Content-type: #{content_type}\r\n"
|
123
|
-
res.socket << "Content-length: #{size}\r\n"
|
124
|
-
res.socket << "\r\n"
|
125
|
-
res.socket << f.read # XXX inefficient for large files, may cause leaks
|
104
|
+
res.status = 200
|
105
|
+
res.body << f.read # XXX inefficient for large files, may cause leaks
|
126
106
|
}
|
127
107
|
return true
|
128
108
|
rescue Object => ex
|
@@ -141,47 +121,30 @@ class MongrelAdapter < ::Mongrel::HttpHandler
|
|
141
121
|
begin
|
142
122
|
context = Context.new(@server)
|
143
123
|
|
144
|
-
context.in =
|
124
|
+
context.in = if req.body.is_a? String
|
125
|
+
StringIO.new(req.body)
|
126
|
+
else
|
127
|
+
req.body
|
128
|
+
end
|
145
129
|
|
146
130
|
context.headers = {}
|
147
131
|
req.params.each { |h, v|
|
148
132
|
if h =~ /\AHTTP_(.*)\Z/
|
149
|
-
|
150
|
-
|
151
|
-
|
133
|
+
context.headers[$1.gsub("_", "-")] = v
|
134
|
+
end
|
135
|
+
context.headers[h] = v
|
152
136
|
}
|
153
|
-
# context.headers.update(req.meta_vars)
|
154
|
-
|
155
|
-
context.headers['REQUEST_URI'] = context.headers['SCRIPT_NAME']
|
156
|
-
|
157
|
-
if context.headers['PATH_INFO'].blank?
|
158
|
-
context.headers['REQUEST_URI'] = '/'
|
159
|
-
else
|
160
|
-
context.headers['REQUEST_URI'] = '/' + context.headers['PATH_INFO']
|
161
|
-
end
|
162
|
-
|
163
|
-
# gmosx: make compatible with fastcgi.
|
164
|
-
|
165
|
-
context.headers['REQUEST_URI'].slice!(/http:\/\/(.*?)\//)
|
166
|
-
context.headers['REQUEST_URI'] = '/' + context.headers['REQUEST_URI']
|
167
137
|
|
168
138
|
Cgi.parse_params(context)
|
169
139
|
Cgi.parse_cookies(context)
|
170
140
|
|
171
141
|
context.render(path)
|
172
|
-
|
173
|
-
res.
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
res.socket << "Unknown Status Code"
|
179
|
-
end
|
180
|
-
res.socket << "\r\n"
|
181
|
-
|
182
|
-
res.socket << Cgi.response_headers(context)
|
183
|
-
res.socket << context.out
|
184
|
-
|
142
|
+
|
143
|
+
res.status = context.status
|
144
|
+
|
145
|
+
res.header.out << Cgi.response_headers(context)
|
146
|
+
res.body << context.out
|
147
|
+
|
185
148
|
context.close
|
186
149
|
ensure
|
187
150
|
Og.manager.put_store if defined?(Og) and Og.respond_to?(:manager) and Og.manager
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'nitro/cgi'
|
2
|
+
|
3
|
+
module Nitro
|
4
|
+
|
5
|
+
# The script adapter. Useful when running in console mode, or
|
6
|
+
# when creating scripts for cron jobs, testing and more. Allows
|
7
|
+
# you to programmatically 'drive' the web application.
|
8
|
+
|
9
|
+
class ScriptAdapter
|
10
|
+
|
11
|
+
# The last generated response.
|
12
|
+
|
13
|
+
attr_accessor :response
|
14
|
+
|
15
|
+
def initialize server
|
16
|
+
@server = server
|
17
|
+
end
|
18
|
+
|
19
|
+
# Perform a programmatic http request to the web app.
|
20
|
+
#
|
21
|
+
# === Examples
|
22
|
+
#
|
23
|
+
# app.get 'users/logout'
|
24
|
+
# app.post 'users/login', :params => { :name => 'gmosx', :password => 'pass' }
|
25
|
+
# app.post 'users/login?name=gmosx;password=pass
|
26
|
+
# app.post 'articles/view/1'
|
27
|
+
|
28
|
+
def handle uri, options = {}
|
29
|
+
context = Context.new(@server)
|
30
|
+
|
31
|
+
begin
|
32
|
+
context.params = options.fetch(:params, {})
|
33
|
+
context.headers = options.fetch(:headers, {})
|
34
|
+
|
35
|
+
context.headers['REQUEST_URI'] = uri
|
36
|
+
context.headers['REQUEST_METHOD'] = options.fetch(:method, :get)
|
37
|
+
context.headers['HTTP_COOKIE'] ||= options[:cookies]
|
38
|
+
|
39
|
+
Cgi.parse_params context
|
40
|
+
Cgi.parse_cookies context
|
41
|
+
|
42
|
+
context.render uri
|
43
|
+
|
44
|
+
context.close
|
45
|
+
ensure
|
46
|
+
$autoreload_dirty = false
|
47
|
+
Og.manager.put_store if defined?(Og) and Og.respond_to?(:manager) and Og.manager
|
48
|
+
end
|
49
|
+
|
50
|
+
@response = context
|
51
|
+
end
|
52
|
+
|
53
|
+
# Perform a programmatic http get request to the web app.
|
54
|
+
|
55
|
+
def get uri, options = {}
|
56
|
+
options[:method] = :get
|
57
|
+
handle uri, options
|
58
|
+
end
|
59
|
+
|
60
|
+
# Perform a programmatic http post request to the web app.
|
61
|
+
|
62
|
+
def post uri, options = {}
|
63
|
+
options[:method] = :post
|
64
|
+
handle uri, options
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
|
71
|
+
# * George Moschovitis <gm@navel.gr>
|