merb-core 0.9.8 → 0.9.9
Sign up to get free protection for your applications and to get access to all the features.
- data/CONTRIBUTORS +33 -0
- data/README +7 -3
- data/Rakefile +3 -3
- data/lib/merb-core.rb +165 -94
- data/lib/merb-core/bootloader.rb +469 -100
- data/lib/merb-core/config.rb +79 -3
- data/lib/merb-core/constants.rb +24 -2
- data/lib/merb-core/controller/abstract_controller.rb +172 -67
- data/lib/merb-core/controller/exceptions.rb +50 -6
- data/lib/merb-core/controller/merb_controller.rb +215 -108
- data/lib/merb-core/controller/mime.rb +36 -12
- data/lib/merb-core/controller/mixins/authentication.rb +52 -7
- data/lib/merb-core/controller/mixins/conditional_get.rb +14 -0
- data/lib/merb-core/controller/mixins/controller.rb +90 -58
- data/lib/merb-core/controller/mixins/render.rb +34 -10
- data/lib/merb-core/controller/mixins/responder.rb +40 -16
- data/lib/merb-core/controller/template.rb +37 -16
- data/lib/merb-core/core_ext/hash.rb +9 -0
- data/lib/merb-core/core_ext/kernel.rb +92 -41
- data/lib/merb-core/dispatch/dispatcher.rb +29 -45
- data/lib/merb-core/dispatch/request.rb +186 -82
- data/lib/merb-core/dispatch/router.rb +141 -53
- data/lib/merb-core/dispatch/router/behavior.rb +296 -139
- data/lib/merb-core/dispatch/router/resources.rb +51 -19
- data/lib/merb-core/dispatch/router/route.rb +76 -23
- data/lib/merb-core/dispatch/session.rb +80 -36
- data/lib/merb-core/dispatch/session/container.rb +31 -15
- data/lib/merb-core/dispatch/session/cookie.rb +51 -22
- data/lib/merb-core/dispatch/session/memcached.rb +10 -6
- data/lib/merb-core/dispatch/session/memory.rb +17 -5
- data/lib/merb-core/dispatch/session/store_container.rb +21 -9
- data/lib/merb-core/dispatch/worker.rb +16 -2
- data/lib/merb-core/gem_ext/erubis.rb +4 -0
- data/lib/merb-core/plugins.rb +13 -0
- data/lib/merb-core/rack.rb +1 -0
- data/lib/merb-core/rack/adapter.rb +1 -0
- data/lib/merb-core/rack/adapter/abstract.rb +95 -17
- data/lib/merb-core/rack/adapter/irb.rb +50 -5
- data/lib/merb-core/rack/application.rb +27 -5
- data/lib/merb-core/rack/handler/mongrel.rb +6 -6
- data/lib/merb-core/rack/helpers.rb +33 -0
- data/lib/merb-core/rack/middleware/conditional_get.rb +1 -1
- data/lib/merb-core/rack/middleware/path_prefix.rb +3 -3
- data/lib/merb-core/rack/middleware/static.rb +11 -7
- data/lib/merb-core/server.rb +134 -69
- data/lib/merb-core/tasks/gem_management.rb +153 -80
- data/lib/merb-core/tasks/merb_rake_helper.rb +12 -4
- data/lib/merb-core/tasks/stats.rake +1 -1
- data/lib/merb-core/test/helpers/mock_request_helper.rb +29 -22
- data/lib/merb-core/test/helpers/request_helper.rb +1 -1
- data/lib/merb-core/test/helpers/route_helper.rb +50 -4
- data/lib/merb-core/test/matchers/request_matchers.rb +2 -36
- data/lib/merb-core/test/matchers/view_matchers.rb +32 -22
- data/lib/merb-core/test/run_specs.rb +6 -5
- data/lib/merb-core/test/test_ext/rspec.rb +6 -19
- data/lib/merb-core/version.rb +1 -1
- metadata +5 -4
@@ -3,10 +3,24 @@ module Merb
|
|
3
3
|
|
4
4
|
attr_accessor :thread
|
5
5
|
|
6
|
+
# ==== Returns
|
7
|
+
# Merb::Worker:: instance of a worker.
|
8
|
+
#
|
9
|
+
# @api private
|
10
|
+
def self.start
|
11
|
+
new
|
12
|
+
end
|
13
|
+
|
14
|
+
# Creates a new worker thread that loops over the work queue.
|
15
|
+
#
|
16
|
+
# @api private
|
6
17
|
def initialize
|
7
18
|
@thread = Thread.new { loop { process_queue } }
|
8
19
|
end
|
9
20
|
|
21
|
+
# Processes tasks in the Merb::Dispatcher.work_queue.
|
22
|
+
#
|
23
|
+
# @api private
|
10
24
|
def process_queue
|
11
25
|
begin
|
12
26
|
while blk = Merb::Dispatcher.work_queue.pop
|
@@ -21,8 +35,8 @@ module Merb
|
|
21
35
|
rescue Exception => e
|
22
36
|
Merb.logger.warn! %Q!Worker Thread Crashed with Exception:\n#{Merb.exception(e)}\nRestarting Worker Thread!
|
23
37
|
retry
|
24
|
-
end
|
38
|
+
end
|
25
39
|
end
|
26
40
|
|
27
41
|
end
|
28
|
-
end
|
42
|
+
end
|
data/lib/merb-core/plugins.rb
CHANGED
@@ -2,10 +2,15 @@ module Merb
|
|
2
2
|
|
3
3
|
module Plugins
|
4
4
|
|
5
|
+
# Returns the configuration settings hash for plugins. This is prepopulated from
|
6
|
+
# Merb.root / "config/plugins.yml" if it is present.
|
7
|
+
#
|
5
8
|
# ==== Returns
|
6
9
|
# Hash::
|
7
10
|
# The configuration loaded from Merb.root / "config/plugins.yml" or, if
|
8
11
|
# the load fails, an empty hash whose default value is another Hash.
|
12
|
+
#
|
13
|
+
# @api plugin
|
9
14
|
def self.config
|
10
15
|
@config ||= begin
|
11
16
|
# this is so you can do Merb.plugins.config[:helpers][:awesome] = "bar"
|
@@ -25,12 +30,16 @@ module Merb
|
|
25
30
|
|
26
31
|
# ==== Returns
|
27
32
|
# Array(String):: All Rakefile load paths Merb uses for plugins.
|
33
|
+
#
|
34
|
+
# @api plugin
|
28
35
|
def self.rakefiles
|
29
36
|
Merb.rakefiles
|
30
37
|
end
|
31
38
|
|
32
39
|
# ==== Returns
|
33
40
|
# Array(String):: All Generator load paths Merb uses for plugins.
|
41
|
+
#
|
42
|
+
# @api plugin
|
34
43
|
def self.generators
|
35
44
|
Merb.generators
|
36
45
|
end
|
@@ -49,6 +58,8 @@ module Merb
|
|
49
58
|
# if defined(Merb::Plugins)
|
50
59
|
# Merb::Plugins.add_rakefiles "merb_sequel" / "merbtasks"
|
51
60
|
# end
|
61
|
+
#
|
62
|
+
# @api plugin
|
52
63
|
def self.add_rakefiles(*rakefiles)
|
53
64
|
Merb.add_rakefiles(*rakefiles)
|
54
65
|
end
|
@@ -60,6 +71,8 @@ module Merb
|
|
60
71
|
#
|
61
72
|
# This is the recommended way to register your plugin's generators
|
62
73
|
# in Merb.
|
74
|
+
#
|
75
|
+
# @api plugin
|
63
76
|
def self.add_generators(*generators)
|
64
77
|
Merb.add_generators(*generators)
|
65
78
|
end
|
data/lib/merb-core/rack.rb
CHANGED
@@ -23,5 +23,6 @@ module Merb
|
|
23
23
|
autoload :ConditionalGet, 'merb-core/rack/middleware/conditional_get'
|
24
24
|
autoload :Csrf, 'merb-core/rack/middleware/csrf'
|
25
25
|
autoload :StreamWrapper, 'merb-core/rack/stream_wrapper'
|
26
|
+
autoload :Helpers, 'merb-core/rack/helpers'
|
26
27
|
end # Rack
|
27
28
|
end # Merb
|
@@ -2,30 +2,83 @@ module Merb
|
|
2
2
|
module Rack
|
3
3
|
class AbstractAdapter
|
4
4
|
|
5
|
+
# This method is designed to be overridden in a rack adapter. It
|
6
|
+
# will be called to start a server created with the new_server method.
|
7
|
+
# This is called from the AbstractAdapter start method.
|
8
|
+
#
|
9
|
+
# @api plugin
|
10
|
+
# @overridable
|
11
|
+
def self.start_server
|
12
|
+
raise NotImplemented
|
13
|
+
end
|
14
|
+
|
15
|
+
# This method is designed to be overridden in a rack adapter. It will
|
16
|
+
# be called to create a new instance of the server for the adapter to
|
17
|
+
# start. The adapter should attempt to bind to a port at this point.
|
18
|
+
# This is called from the AbstractAdapter start method.
|
19
|
+
#
|
20
|
+
# ==== Parameters
|
21
|
+
# port<Integer>:: The port the server should listen on
|
22
|
+
#
|
23
|
+
# @api plugin
|
24
|
+
# @overridable
|
25
|
+
def self.new_server(port)
|
26
|
+
raise NotImplemented
|
27
|
+
end
|
28
|
+
|
29
|
+
# This method is designed to be overridden in a rack adapter. It will
|
30
|
+
# be called to stop the adapter server.
|
31
|
+
#
|
32
|
+
# ==== Parameters
|
33
|
+
# status<Integer>:: The exit status the adapter should exit with.
|
34
|
+
#
|
35
|
+
# ==== Returns
|
36
|
+
# Boolean:: True if the server was properly stopped.
|
37
|
+
#
|
38
|
+
# @api plugin
|
39
|
+
# @overridable
|
40
|
+
def self.stop(status)
|
41
|
+
raise NotImplemented
|
42
|
+
end
|
43
|
+
|
5
44
|
# Spawn a new worker process at a port.
|
45
|
+
#
|
46
|
+
# ==== Parameters
|
47
|
+
# port<Integer>:: The port to start the worker process on.
|
48
|
+
#
|
49
|
+
# @api private
|
6
50
|
def self.spawn_worker(port)
|
7
|
-
|
8
|
-
start_at_port(port, @opts) unless
|
51
|
+
worker_pid = Kernel.fork
|
52
|
+
start_at_port(port, @opts) unless worker_pid
|
9
53
|
|
10
|
-
# If we have a
|
11
|
-
throw(:new_worker) unless
|
54
|
+
# If we have a worker_pid, we're in the parent.
|
55
|
+
throw(:new_worker) unless worker_pid
|
12
56
|
|
13
|
-
@pids[port] =
|
14
|
-
$
|
57
|
+
@pids[port] = worker_pid
|
58
|
+
$WORKERS = @pids.values
|
15
59
|
end
|
16
60
|
|
17
61
|
# The main start method for bootloaders that support forking.
|
62
|
+
# This method launches the adapters which inherit using the
|
63
|
+
# new_server and start_server methods. This method should not
|
64
|
+
# be overridden in adapters which want to fork.
|
65
|
+
#
|
66
|
+
# ==== Parameters
|
67
|
+
# opts<Hash>:: A hash of options
|
68
|
+
# socket: the socket to bind to
|
69
|
+
# port: the port to bind to
|
70
|
+
# cluster: the number
|
71
|
+
#
|
72
|
+
# @api private
|
18
73
|
def self.start(opts={})
|
19
74
|
@opts = opts
|
20
|
-
$
|
75
|
+
$WORKERS ||= []
|
21
76
|
parent = nil
|
22
77
|
|
23
78
|
@pids = {}
|
24
79
|
port = (opts[:socket] || opts[:port]).to_i
|
25
80
|
max_port = Merb::Config[:cluster] ? Merb::Config[:cluster] - 1 : 0
|
26
81
|
|
27
|
-
Merb.logger.warn! "Cluster: #{max_port}"
|
28
|
-
|
29
82
|
# If we only have a single merb, just start it up and dispense with
|
30
83
|
# the spawner/worker setup.
|
31
84
|
if max_port == 0
|
@@ -36,7 +89,7 @@ module Merb
|
|
36
89
|
$0 = process_title(:spawner, port)
|
37
90
|
|
38
91
|
# For each port, spawn a new worker. The parent will continue in
|
39
|
-
# the loop, while the
|
92
|
+
# the loop, while the worker will throw :new_worker and be booted
|
40
93
|
# out of the loop.
|
41
94
|
catch(:new_worker) do
|
42
95
|
0.upto(max_port) do |i|
|
@@ -64,7 +117,7 @@ module Merb
|
|
64
117
|
ensure
|
65
118
|
# If there was no worker with that PID, the status was non-0
|
66
119
|
# (we send back a status of 128 when ABRT is called on a
|
67
|
-
#
|
120
|
+
# worker, and Merb.fatal! exits with a status of 1), or if
|
68
121
|
# Merb is in the process of exiting, *then* don't respawn.
|
69
122
|
# Note that processes killed with kill -9 will return no
|
70
123
|
# exitstatus, and we respawn them.
|
@@ -88,11 +141,21 @@ module Merb
|
|
88
141
|
|
89
142
|
end
|
90
143
|
|
144
|
+
# Fork a server on the specified port and start the app.
|
145
|
+
#
|
146
|
+
# ==== Parameters
|
147
|
+
# port<Integer>:: The port to start the server on
|
148
|
+
# opts<Hash>:: The hash of options, defaults to the @opts
|
149
|
+
# instance variable.
|
150
|
+
#
|
151
|
+
# @api private
|
91
152
|
def self.start_at_port(port, opts = @opts)
|
92
153
|
at_exit do
|
93
154
|
Merb::Server.remove_pid(port)
|
94
155
|
end
|
95
156
|
|
157
|
+
Merb::Worker.start
|
158
|
+
|
96
159
|
# If Merb is daemonized, trap INT. If it's not daemonized,
|
97
160
|
# we let the master process' ctrl-c control the cluster
|
98
161
|
# of workers.
|
@@ -111,6 +174,7 @@ module Merb
|
|
111
174
|
# In daemonized mode or not, support HUPing the process to
|
112
175
|
# restart it.
|
113
176
|
Merb.trap('HUP') do
|
177
|
+
Merb.exiting = true
|
114
178
|
stop
|
115
179
|
Merb.logger.warn! "Exiting port #{port} on #{Process.pid}\n"
|
116
180
|
exit_process
|
@@ -118,6 +182,7 @@ module Merb
|
|
118
182
|
|
119
183
|
# ABRTing the process will kill it, and it will not be respawned.
|
120
184
|
Merb.trap('ABRT') do
|
185
|
+
Merb.exiting = true
|
121
186
|
stopped = stop(128)
|
122
187
|
Merb.logger.warn! "Exiting port #{port}\n" if stopped
|
123
188
|
exit_process(128)
|
@@ -129,9 +194,9 @@ module Merb
|
|
129
194
|
# Store the PID for this worker
|
130
195
|
Merb::Server.store_pid(port)
|
131
196
|
|
132
|
-
Merb::Config[:log_delimiter] = "#{
|
197
|
+
Merb::Config[:log_delimiter] = "#{process_title(:worker, port)} ~ "
|
133
198
|
|
134
|
-
Merb.
|
199
|
+
Merb.reset_logger!
|
135
200
|
Merb.logger.warn!("Starting #{self.name.split("::").last} at port #{port}")
|
136
201
|
|
137
202
|
# If we can't connect to the port, keep trying until we can. Print
|
@@ -144,8 +209,8 @@ module Merb
|
|
144
209
|
new_server(port)
|
145
210
|
rescue Errno::EADDRINUSE
|
146
211
|
unless printed_warning
|
147
|
-
Merb.logger.warn! "
|
148
|
-
|
212
|
+
Merb.logger.warn! "Port #{port} is in use, " \
|
213
|
+
"Waiting for it to become available."
|
149
214
|
printed_warning = true
|
150
215
|
end
|
151
216
|
|
@@ -163,11 +228,24 @@ module Merb
|
|
163
228
|
start_server
|
164
229
|
end
|
165
230
|
|
166
|
-
#
|
231
|
+
# Exit the process with the specified status.
|
232
|
+
#
|
233
|
+
# ==== Parameters
|
234
|
+
# status<Integer>:: The exit code of the process.
|
235
|
+
#
|
236
|
+
# @api private
|
167
237
|
def self.exit_process(status = 0)
|
168
238
|
exit(status)
|
169
239
|
end
|
170
240
|
|
241
|
+
# Set the process title.
|
242
|
+
#
|
243
|
+
# ==== Parameters
|
244
|
+
# whoami<Symbol>:: Either :spawner for the master process or :worker for any of the worker
|
245
|
+
# processes.
|
246
|
+
# port<Integer>:: The base port that the app is running on.
|
247
|
+
#
|
248
|
+
# @api private
|
171
249
|
def self.process_title(whoami, port)
|
172
250
|
name = Merb::Config[:name]
|
173
251
|
app = "merb#{" : #{name}" if (name && name != "merb")}"
|
@@ -179,7 +257,7 @@ module Merb
|
|
179
257
|
"socket#{'s' if max_port > 0 && whoami != :worker} #{numbers} "\
|
180
258
|
"#{file ? file : "#{Merb.log_path}/#{name}.#{port}.sock"}"
|
181
259
|
else
|
182
|
-
"port#{'s' if max_port > 0 && whoami != :worker}"
|
260
|
+
"port#{'s' if max_port > 0 && whoami != :worker} #{port}"
|
183
261
|
end
|
184
262
|
"#{app} : #{whoami} (#{listening_on})"
|
185
263
|
end
|
@@ -1,15 +1,60 @@
|
|
1
1
|
module Merb
|
2
2
|
module Rack
|
3
3
|
class Console
|
4
|
-
#
|
5
|
-
#
|
6
|
-
#
|
4
|
+
# There are three possible ways to use this method. First, if you have a named route,
|
5
|
+
# you can specify the route as the first parameter as a symbol and any paramters in a
|
6
|
+
# hash. Second, you can generate the default route by just passing the params hash,
|
7
|
+
# just passing the params hash. Finally, you can use the anonymous parameters. This
|
8
|
+
# allows you to specify the parameters to a named route in the order they appear in the
|
9
|
+
# router.
|
10
|
+
#
|
11
|
+
# ==== Parameters(Named Route)
|
12
|
+
# name<Symbol>::
|
13
|
+
# The name of the route.
|
14
|
+
# args<Hash>::
|
15
|
+
# Parameters for the route generation.
|
16
|
+
#
|
17
|
+
# ==== Parameters(Default Route)
|
18
|
+
# args<Hash>::
|
19
|
+
# Parameters for the route generation. This route will use the default route.
|
20
|
+
#
|
21
|
+
# ==== Parameters(Anonymous Parameters)
|
22
|
+
# name<Symbol>::
|
23
|
+
# The name of the route.
|
24
|
+
# args<Array>::
|
25
|
+
# An array of anonymous parameters to generate the route
|
26
|
+
# with. These parameters are assigned to the route parameters
|
27
|
+
# in the order that they are passed.
|
7
28
|
#
|
8
29
|
# ==== Returns
|
9
30
|
# String:: The generated URL.
|
10
31
|
#
|
11
|
-
# ====
|
12
|
-
#
|
32
|
+
# ==== Examples
|
33
|
+
# Named Route
|
34
|
+
#
|
35
|
+
# Merb::Router.prepare do
|
36
|
+
# match("/articles/:title").to(:controller => :articles, :action => :show).name("articles")
|
37
|
+
# end
|
38
|
+
#
|
39
|
+
# url(:articles, :title => "new_article")
|
40
|
+
#
|
41
|
+
# Default Route
|
42
|
+
#
|
43
|
+
# Merb::Router.prepare do
|
44
|
+
# default_routes
|
45
|
+
# end
|
46
|
+
#
|
47
|
+
# url(:controller => "articles", :action => "new")
|
48
|
+
#
|
49
|
+
# Anonymous Paramters
|
50
|
+
#
|
51
|
+
# Merb::Router.prepare do
|
52
|
+
# match("/articles/:year/:month/:title").to(:controller => :articles, :action => :show).name("articles")
|
53
|
+
# end
|
54
|
+
#
|
55
|
+
# url(:articles, 2008, 10, "test_article")
|
56
|
+
#
|
57
|
+
# @api public
|
13
58
|
def url(name, *args)
|
14
59
|
args << {}
|
15
60
|
Merb::Router.url(name, *args)
|
@@ -2,24 +2,46 @@ module Merb
|
|
2
2
|
module Rack
|
3
3
|
class Application
|
4
4
|
|
5
|
+
# The main rack application call method. This is the entry point from rack (and the webserver)
|
6
|
+
# to your application.
|
7
|
+
#
|
8
|
+
# ==== Parameters
|
9
|
+
# env<Hash>:: A rack request of parameters.
|
10
|
+
#
|
11
|
+
# ==== Returns
|
12
|
+
# <Array>:: A rack response of [status<Integer>, headers<Hash>, body<String, Stream>]
|
13
|
+
#
|
14
|
+
# @api private
|
5
15
|
def call(env)
|
6
16
|
begin
|
7
|
-
|
17
|
+
rack_response = ::Merb::Dispatcher.handle(Merb::Request.new(env))
|
8
18
|
rescue Object => e
|
9
|
-
return [500, {Merb::Const::CONTENT_TYPE =>
|
19
|
+
return [500, {Merb::Const::CONTENT_TYPE => Merb::Const::TEXT_SLASH_HTML}, e.message + Merb::Const::BREAK_TAG + e.backtrace.join(Merb::Const::BREAK_TAG)]
|
10
20
|
end
|
11
|
-
Merb.logger.info
|
21
|
+
Merb.logger.info Merb::Const::DOUBLE_NEWLINE
|
12
22
|
Merb.logger.flush
|
13
23
|
|
14
24
|
# unless controller.headers[Merb::Const::DATE]
|
15
25
|
# require "time"
|
16
26
|
# controller.headers[Merb::Const::DATE] = Time.now.rfc2822.to_s
|
17
27
|
# end
|
18
|
-
|
28
|
+
rack_response
|
19
29
|
end
|
20
30
|
|
31
|
+
# Determines whether this request is a "deferred_action", usually a long request.
|
32
|
+
# Rack uses this method to detemine whether to use an evented request or a deferred
|
33
|
+
# request in evented rack handlers.
|
34
|
+
#
|
35
|
+
# ==== Parameters
|
36
|
+
# env<Hash>:: The rack request
|
37
|
+
#
|
38
|
+
# ==== Returns
|
39
|
+
# Boolean::
|
40
|
+
# True if the request should be deferred.
|
41
|
+
#
|
42
|
+
# @api private
|
21
43
|
def deferred?(env)
|
22
|
-
path = env[Merb::Const::PATH_INFO] ? env[Merb::Const::PATH_INFO].chomp(
|
44
|
+
path = env[Merb::Const::PATH_INFO] ? env[Merb::Const::PATH_INFO].chomp(Merb::Const::SLASH) : Merb::Const::EMPTY_STRING
|
23
45
|
if path =~ Merb.deferred_actions
|
24
46
|
Merb.logger.info! "Deferring Request: #{path}"
|
25
47
|
true
|
@@ -54,10 +54,10 @@ module Merb
|
|
54
54
|
# response<HTTPResponse>:: The response object to write response to.
|
55
55
|
def process(request, response)
|
56
56
|
env = {}.replace(request.params)
|
57
|
-
env.delete
|
58
|
-
env.delete
|
57
|
+
env.delete Merb::Const::HTTP_CONTENT_TYPE
|
58
|
+
env.delete Merb::Const::HTTP_CONTENT_LENGTH
|
59
59
|
|
60
|
-
env[
|
60
|
+
env[Merb::Const::SCRIPT_NAME] = Merb::Const::EMPTY_STRING if env[Merb::Const::SCRIPT_NAME] == Merb::Const::SLASH
|
61
61
|
|
62
62
|
env.update({"rack.version" => [0,1],
|
63
63
|
"rack.input" => request.body || StringIO.new(""),
|
@@ -69,8 +69,8 @@ module Merb
|
|
69
69
|
|
70
70
|
"rack.url_scheme" => "http"
|
71
71
|
})
|
72
|
-
env[
|
73
|
-
env.delete
|
72
|
+
env[Merb::Const::QUERY_STRING] ||= ""
|
73
|
+
env.delete Merb::Const::PATH_INFO if env[Merb::Const::PATH_INFO] == Merb::Const::EMPTY_STRING
|
74
74
|
|
75
75
|
status, headers, body = @app.call(env)
|
76
76
|
|
@@ -93,4 +93,4 @@ module Merb
|
|
93
93
|
end
|
94
94
|
end
|
95
95
|
end
|
96
|
-
end
|
96
|
+
end
|