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 CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  TITLE : &title Nitro
4
4
  NAME : &pkg nitro
5
- VERSION : '0.30.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.0' ]
30
- - [ gen, '= 0.30.0' ]
31
- - [ glue, '= 0.30.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, '= 0.5' ]
34
- - [ daemons, '= 0.4.2' ]
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
- $NITRO_NO_ENVIRONMENT = true
3
+ require 'facets/more/consoleapp'
4
4
 
5
- require 'nitro'
5
+ class NitroCommand < Console::Command
6
6
 
7
- if File.exist?('run.rb')
8
- system('ruby -rubygems run.rb')
9
- else
10
- puts 'No application found, starting default application.'
11
- system 'ruby -rubygems ' + File.join(Nitro::LibPath, '..', 'proto', 'run.rb')
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 ::Aspects
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
- Logger.debug "Sweeper expired cache file '#{Server.public_root}/#{name}'" if $DBG
30
- FileUtils.rm_rf("#{Server.public_root}/#{name}")
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.30.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
@@ -0,0 +1,7 @@
1
+ require 'nitro/adapter/script'
2
+
3
+ module Nitro
4
+ ConsoleAdapter = ScriptAdapter
5
+ end
6
+
7
+ # * George Moschovitis <gm@navel.gr>
@@ -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
- mongrel_options = server.options.dup
44
+ mongrel_options = server.options.dup
46
45
 
47
- mongrel_options.update(
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
- mongrel_options[:Port])
54
-
55
- trap('INT') { stop } # will this work?
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
- @mongrel_thread.kill
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
- size = File.size(filename)
121
- res.socket << "HTTP/1.1 200 OK\r\n"
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 = StringIO.new(req.body || "")
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
- context.headers[$1.gsub("_", "-")] = v
150
- end
151
- context.headers[h] = v
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.socket << "HTTP/1.1 #{context.status.to_s} "
174
-
175
- if STATUS_CODES.has_key? context.status
176
- res.socket << STATUS_CODES[context.status]
177
- else
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>