nitro 0.30.0 → 0.31.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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>