merb-core 0.9.2
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/LICENSE +20 -0
- data/README +21 -0
- data/Rakefile +285 -0
- data/TODO +0 -0
- data/bin/merb +8 -0
- data/bin/merb-specs +5 -0
- data/docs/bootloading.dox +57 -0
- data/docs/documentation_standards +40 -0
- data/docs/new_render_api +51 -0
- data/lib/merb-core.rb +304 -0
- data/lib/merb-core/autoload.rb +29 -0
- data/lib/merb-core/bootloader.rb +601 -0
- data/lib/merb-core/config.rb +284 -0
- data/lib/merb-core/constants.rb +43 -0
- data/lib/merb-core/controller/abstract_controller.rb +531 -0
- data/lib/merb-core/controller/exceptions.rb +257 -0
- data/lib/merb-core/controller/merb_controller.rb +214 -0
- data/lib/merb-core/controller/mime.rb +88 -0
- data/lib/merb-core/controller/mixins/controller.rb +262 -0
- data/lib/merb-core/controller/mixins/render.rb +324 -0
- data/lib/merb-core/controller/mixins/responder.rb +464 -0
- data/lib/merb-core/controller/template.rb +205 -0
- data/lib/merb-core/core_ext.rb +12 -0
- data/lib/merb-core/core_ext/class.rb +192 -0
- data/lib/merb-core/core_ext/hash.rb +422 -0
- data/lib/merb-core/core_ext/kernel.rb +304 -0
- data/lib/merb-core/core_ext/mash.rb +154 -0
- data/lib/merb-core/core_ext/object.rb +136 -0
- data/lib/merb-core/core_ext/object_space.rb +14 -0
- data/lib/merb-core/core_ext/rubygems.rb +28 -0
- data/lib/merb-core/core_ext/set.rb +41 -0
- data/lib/merb-core/core_ext/string.rb +69 -0
- data/lib/merb-core/dispatch/cookies.rb +92 -0
- data/lib/merb-core/dispatch/dispatcher.rb +233 -0
- data/lib/merb-core/dispatch/exceptions.html.erb +297 -0
- data/lib/merb-core/dispatch/request.rb +560 -0
- data/lib/merb-core/dispatch/router.rb +141 -0
- data/lib/merb-core/dispatch/router/behavior.rb +777 -0
- data/lib/merb-core/dispatch/router/cached_proc.rb +52 -0
- data/lib/merb-core/dispatch/router/route.rb +212 -0
- data/lib/merb-core/dispatch/session.rb +28 -0
- data/lib/merb-core/dispatch/session/cookie.rb +166 -0
- data/lib/merb-core/dispatch/session/memcached.rb +161 -0
- data/lib/merb-core/dispatch/session/memory.rb +234 -0
- data/lib/merb-core/gem_ext/erubis.rb +19 -0
- data/lib/merb-core/logger.rb +230 -0
- data/lib/merb-core/plugins.rb +25 -0
- data/lib/merb-core/rack.rb +15 -0
- data/lib/merb-core/rack/adapter.rb +42 -0
- data/lib/merb-core/rack/adapter/ebb.rb +22 -0
- data/lib/merb-core/rack/adapter/evented_mongrel.rb +24 -0
- data/lib/merb-core/rack/adapter/fcgi.rb +16 -0
- data/lib/merb-core/rack/adapter/irb.rb +108 -0
- data/lib/merb-core/rack/adapter/mongrel.rb +25 -0
- data/lib/merb-core/rack/adapter/runner.rb +27 -0
- data/lib/merb-core/rack/adapter/thin.rb +27 -0
- data/lib/merb-core/rack/adapter/webrick.rb +35 -0
- data/lib/merb-core/rack/application.rb +77 -0
- data/lib/merb-core/rack/handler/mongrel.rb +97 -0
- data/lib/merb-core/server.rb +184 -0
- data/lib/merb-core/test.rb +10 -0
- data/lib/merb-core/test/helpers.rb +9 -0
- data/lib/merb-core/test/helpers/controller_helper.rb +8 -0
- data/lib/merb-core/test/helpers/multipart_request_helper.rb +175 -0
- data/lib/merb-core/test/helpers/request_helper.rb +257 -0
- data/lib/merb-core/test/helpers/route_helper.rb +33 -0
- data/lib/merb-core/test/helpers/view_helper.rb +121 -0
- data/lib/merb-core/test/matchers.rb +9 -0
- data/lib/merb-core/test/matchers/controller_matchers.rb +269 -0
- data/lib/merb-core/test/matchers/route_matchers.rb +136 -0
- data/lib/merb-core/test/matchers/view_matchers.rb +293 -0
- data/lib/merb-core/test/run_specs.rb +38 -0
- data/lib/merb-core/test/tasks/spectasks.rb +39 -0
- data/lib/merb-core/test/test_ext/hpricot.rb +32 -0
- data/lib/merb-core/test/test_ext/object.rb +14 -0
- data/lib/merb-core/vendor/facets.rb +2 -0
- data/lib/merb-core/vendor/facets/dictionary.rb +433 -0
- data/lib/merb-core/vendor/facets/inflect.rb +211 -0
- data/lib/merb-core/version.rb +11 -0
- data/spec/private/config/adapter_spec.rb +32 -0
- data/spec/private/config/config_spec.rb +139 -0
- data/spec/private/config/environment_spec.rb +13 -0
- data/spec/private/config/spec_helper.rb +1 -0
- data/spec/private/core_ext/hash_spec.rb +506 -0
- data/spec/private/core_ext/kernel_spec.rb +46 -0
- data/spec/private/core_ext/object_spec.rb +39 -0
- data/spec/private/core_ext/set_spec.rb +26 -0
- data/spec/private/core_ext/string_spec.rb +9 -0
- data/spec/private/dispatch/cookies_spec.rb +107 -0
- data/spec/private/dispatch/dispatch_spec.rb +26 -0
- data/spec/private/dispatch/fixture/app/controllers/application.rb +4 -0
- data/spec/private/dispatch/fixture/app/controllers/exceptions.rb +27 -0
- data/spec/private/dispatch/fixture/app/controllers/foo.rb +21 -0
- data/spec/private/dispatch/fixture/app/helpers/global_helpers.rb +8 -0
- data/spec/private/dispatch/fixture/app/views/exeptions/client_error.html.erb +37 -0
- data/spec/private/dispatch/fixture/app/views/exeptions/internal_server_error.html.erb +216 -0
- data/spec/private/dispatch/fixture/app/views/exeptions/not_acceptable.html.erb +38 -0
- data/spec/private/dispatch/fixture/app/views/exeptions/not_found.html.erb +40 -0
- data/spec/private/dispatch/fixture/app/views/foo/bar.html.erb +0 -0
- data/spec/private/dispatch/fixture/app/views/layout/application.html.erb +11 -0
- data/spec/private/dispatch/fixture/config/environments/development.rb +6 -0
- data/spec/private/dispatch/fixture/config/environments/production.rb +5 -0
- data/spec/private/dispatch/fixture/config/environments/test.rb +6 -0
- data/spec/private/dispatch/fixture/config/init.rb +45 -0
- data/spec/private/dispatch/fixture/config/rack.rb +1 -0
- data/spec/private/dispatch/fixture/config/router.rb +35 -0
- data/spec/private/dispatch/fixture/log/development.log +1 -0
- data/spec/private/dispatch/fixture/log/merb.4000.pid +1 -0
- data/spec/private/dispatch/fixture/log/merb_test.log +2040 -0
- data/spec/private/dispatch/fixture/log/production.log +1 -0
- data/spec/private/dispatch/fixture/merb.4000.pid +1 -0
- data/spec/private/dispatch/fixture/public/images/merb.jpg +0 -0
- data/spec/private/dispatch/fixture/public/merb.fcgi +4 -0
- data/spec/private/dispatch/fixture/public/stylesheets/master.css +119 -0
- data/spec/private/dispatch/route_params_spec.rb +24 -0
- data/spec/private/dispatch/spec_helper.rb +1 -0
- data/spec/private/plugins/plugin_spec.rb +81 -0
- data/spec/private/rack/application_spec.rb +43 -0
- data/spec/public/DEFINITIONS +11 -0
- data/spec/public/abstract_controller/controllers/alt_views/layout/application.erb +1 -0
- data/spec/public/abstract_controller/controllers/alt_views/layout/merb/test/fixtures/abstract/render_string_controller_layout.erb +1 -0
- data/spec/public/abstract_controller/controllers/alt_views/layout/merb/test/fixtures/abstract/render_template_controller_layout.erb +1 -0
- data/spec/public/abstract_controller/controllers/alt_views/merb/test/fixtures/abstract/display_object_with_multiple_roots/index.erb +1 -0
- data/spec/public/abstract_controller/controllers/alt_views/merb/test/fixtures/abstract/display_object_with_multiple_roots/show.erb +1 -0
- data/spec/public/abstract_controller/controllers/alt_views/merb/test/fixtures/abstract/render_template_multiple_roots/index.erb +1 -0
- data/spec/public/abstract_controller/controllers/alt_views/partial/basic_partial_with_multiple_roots/_partial.erb +1 -0
- data/spec/public/abstract_controller/controllers/alt_views/render_template_multiple_roots_and_custom_location/index.erb +1 -0
- data/spec/public/abstract_controller/controllers/alt_views/render_template_multiple_roots_inherited/index.erb +1 -0
- data/spec/public/abstract_controller/controllers/display.rb +54 -0
- data/spec/public/abstract_controller/controllers/filters.rb +167 -0
- data/spec/public/abstract_controller/controllers/helpers.rb +31 -0
- data/spec/public/abstract_controller/controllers/partial.rb +106 -0
- data/spec/public/abstract_controller/controllers/render.rb +86 -0
- data/spec/public/abstract_controller/controllers/views/helpers/capture/index.erb +1 -0
- data/spec/public/abstract_controller/controllers/views/helpers/concat/index.erb +1 -0
- data/spec/public/abstract_controller/controllers/views/layout/alt.erb +1 -0
- data/spec/public/abstract_controller/controllers/views/layout/custom.erb +1 -0
- data/spec/public/abstract_controller/controllers/views/merb/test/fixtures/abstract/display_object/index.erb +1 -0
- data/spec/public/abstract_controller/controllers/views/merb/test/fixtures/abstract/display_object_with_action/new.erb +1 -0
- data/spec/public/abstract_controller/controllers/views/merb/test/fixtures/abstract/render_template/index.erb +1 -0
- data/spec/public/abstract_controller/controllers/views/merb/test/fixtures/abstract/render_template_app_layout/index.erb +0 -0
- data/spec/public/abstract_controller/controllers/views/merb/test/fixtures/abstract/render_template_custom_layout/index.erb +1 -0
- data/spec/public/abstract_controller/controllers/views/merb/test/fixtures/abstract/render_template_multiple_roots/index.erb +1 -0
- data/spec/public/abstract_controller/controllers/views/merb/test/fixtures/abstract/render_template_multiple_roots/show.erb +1 -0
- data/spec/public/abstract_controller/controllers/views/partial/another_directory/_partial.erb +1 -0
- data/spec/public/abstract_controller/controllers/views/partial/basic_partial/_partial.erb +1 -0
- data/spec/public/abstract_controller/controllers/views/partial/basic_partial/index.erb +1 -0
- data/spec/public/abstract_controller/controllers/views/partial/basic_partial_with_multiple_roots/index.erb +1 -0
- data/spec/public/abstract_controller/controllers/views/partial/nested_partial/_first.erb +1 -0
- data/spec/public/abstract_controller/controllers/views/partial/nested_partial/_second.erb +1 -0
- data/spec/public/abstract_controller/controllers/views/partial/nested_partial/index.erb +1 -0
- data/spec/public/abstract_controller/controllers/views/partial/partial_in_another_directory/index.erb +1 -0
- data/spec/public/abstract_controller/controllers/views/partial/partial_with_both/_collection.erb +1 -0
- data/spec/public/abstract_controller/controllers/views/partial/partial_with_both/index.erb +1 -0
- data/spec/public/abstract_controller/controllers/views/partial/partial_with_collections/_collection.erb +1 -0
- data/spec/public/abstract_controller/controllers/views/partial/partial_with_collections/index.erb +1 -0
- data/spec/public/abstract_controller/controllers/views/partial/partial_with_collections_and_as/_collection.erb +1 -0
- data/spec/public/abstract_controller/controllers/views/partial/partial_with_collections_and_as/index.erb +1 -0
- data/spec/public/abstract_controller/controllers/views/partial/partial_with_locals/_variables.erb +1 -0
- data/spec/public/abstract_controller/controllers/views/partial/partial_with_locals/index.erb +1 -0
- data/spec/public/abstract_controller/controllers/views/partial/partial_with_with_and_locals/_both.erb +1 -0
- data/spec/public/abstract_controller/controllers/views/partial/partial_with_with_and_locals/index.erb +1 -0
- data/spec/public/abstract_controller/controllers/views/partial/with_as_partial/_with_partial.erb +1 -0
- data/spec/public/abstract_controller/controllers/views/partial/with_as_partial/index.erb +1 -0
- data/spec/public/abstract_controller/controllers/views/partial/with_nil_partial/_with_partial.erb +1 -0
- data/spec/public/abstract_controller/controllers/views/partial/with_nil_partial/index.erb +1 -0
- data/spec/public/abstract_controller/controllers/views/partial/with_partial/_with_partial.erb +1 -0
- data/spec/public/abstract_controller/controllers/views/partial/with_partial/index.erb +1 -0
- data/spec/public/abstract_controller/controllers/views/test_display/foo.html.erb +1 -0
- data/spec/public/abstract_controller/controllers/views/test_render/foo.html.erb +0 -0
- data/spec/public/abstract_controller/controllers/views/wonderful/index.erb +1 -0
- data/spec/public/abstract_controller/display_spec.rb +33 -0
- data/spec/public/abstract_controller/filter_spec.rb +80 -0
- data/spec/public/abstract_controller/helper_spec.rb +13 -0
- data/spec/public/abstract_controller/partial_spec.rb +53 -0
- data/spec/public/abstract_controller/render_spec.rb +70 -0
- data/spec/public/abstract_controller/spec_helper.rb +27 -0
- data/spec/public/boot_loader/boot_loader_spec.rb +33 -0
- data/spec/public/boot_loader/spec_helper.rb +1 -0
- data/spec/public/controller/base_spec.rb +31 -0
- data/spec/public/controller/controllers/base.rb +41 -0
- data/spec/public/controller/controllers/display.rb +40 -0
- data/spec/public/controller/controllers/responder.rb +67 -0
- data/spec/public/controller/controllers/url.rb +7 -0
- data/spec/public/controller/controllers/views/layout/custom.html.erb +1 -0
- data/spec/public/controller/controllers/views/merb/test/fixtures/controllers/class_provides/index.html.erb +1 -0
- data/spec/public/controller/controllers/views/merb/test/fixtures/controllers/class_provides/index.xml.erb +1 -0
- data/spec/public/controller/controllers/views/merb/test/fixtures/controllers/display_with_template/index.html.erb +1 -0
- data/spec/public/controller/controllers/views/merb/test/fixtures/controllers/html_default/index.html.erb +1 -0
- data/spec/public/controller/controllers/views/merb/test/fixtures/controllers/layout/custom.html.erb +1 -0
- data/spec/public/controller/controllers/views/merb/test/fixtures/controllers/local_provides/index.html.erb +1 -0
- data/spec/public/controller/controllers/views/merb/test/fixtures/controllers/local_provides/index.xml.erb +1 -0
- data/spec/public/controller/controllers/views/merb/test/fixtures/controllers/multi_provides/index.html.erb +1 -0
- data/spec/public/controller/controllers/views/merb/test/fixtures/controllers/multi_provides/index.js.erb +1 -0
- data/spec/public/controller/display_spec.rb +34 -0
- data/spec/public/controller/log/merb.4000.pid +1 -0
- data/spec/public/controller/responder_spec.rb +95 -0
- data/spec/public/controller/spec_helper.rb +9 -0
- data/spec/public/controller/url_spec.rb +152 -0
- data/spec/public/directory_structure/directory/app/controllers/application.rb +3 -0
- data/spec/public/directory_structure/directory/app/controllers/base.rb +13 -0
- data/spec/public/directory_structure/directory/app/controllers/custom.rb +19 -0
- data/spec/public/directory_structure/directory/app/views/base/template.html.erb +1 -0
- data/spec/public/directory_structure/directory/app/views/wonderful/template.erb +1 -0
- data/spec/public/directory_structure/directory/config/router.rb +3 -0
- data/spec/public/directory_structure/directory/log/merb.4000.pid +1 -0
- data/spec/public/directory_structure/directory/log/merb_test.log +265 -0
- data/spec/public/directory_structure/directory/merb.4000.pid +1 -0
- data/spec/public/directory_structure/directory_spec.rb +44 -0
- data/spec/public/logger/logger_spec.rb +175 -0
- data/spec/public/logger/spec_helper.rb +1 -0
- data/spec/public/reloading/directory/app/controllers/application.rb +3 -0
- data/spec/public/reloading/directory/app/controllers/reload.rb +6 -0
- data/spec/public/reloading/directory/config/init.rb +2 -0
- data/spec/public/reloading/directory/log/merb.4000.pid +1 -0
- data/spec/public/reloading/directory/log/merb_test.log +59 -0
- data/spec/public/reloading/directory/merb.4000.pid +1 -0
- data/spec/public/reloading/reload_spec.rb +80 -0
- data/spec/public/request/multipart_spec.rb +15 -0
- data/spec/public/request/request_spec.rb +207 -0
- data/spec/public/router/default_spec.rb +21 -0
- data/spec/public/router/deferred_spec.rb +22 -0
- data/spec/public/router/namespace_spec.rb +113 -0
- data/spec/public/router/nested_resources_spec.rb +34 -0
- data/spec/public/router/resource_spec.rb +45 -0
- data/spec/public/router/resources_spec.rb +57 -0
- data/spec/public/router/spec_helper.rb +72 -0
- data/spec/public/router/special_spec.rb +44 -0
- data/spec/public/router/string_spec.rb +61 -0
- data/spec/public/template/template_spec.rb +92 -0
- data/spec/public/template/templates/error.html.erb +2 -0
- data/spec/public/template/templates/template.html.erb +1 -0
- data/spec/public/template/templates/template.html.myt +1 -0
- data/spec/public/test/controller_matchers_spec.rb +378 -0
- data/spec/public/test/controllers/controller_assertion_mock.rb +7 -0
- data/spec/public/test/controllers/dispatch_controller.rb +11 -0
- data/spec/public/test/controllers/spec_helper_controller.rb +30 -0
- data/spec/public/test/multipart_request_helper_spec.rb +159 -0
- data/spec/public/test/multipart_upload_text_file.txt +1 -0
- data/spec/public/test/request_helper_spec.rb +153 -0
- data/spec/public/test/route_helper_spec.rb +54 -0
- data/spec/public/test/route_matchers_spec.rb +133 -0
- data/spec/public/test/view_helper_spec.rb +96 -0
- data/spec/public/test/view_matchers_spec.rb +107 -0
- data/spec/spec_helper.rb +71 -0
- metadata +488 -0
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
require 'stringio'
|
|
2
|
+
class Mongrel::HttpResponse
|
|
3
|
+
NO_CLOSE_STATUS_FORMAT = "HTTP/1.1 %d %s\r\n".freeze
|
|
4
|
+
|
|
5
|
+
# Sends the status to the client without closing the connection.
|
|
6
|
+
#
|
|
7
|
+
# ==== Parameters
|
|
8
|
+
# content_length<Fixnum>:: The length of the content. Defaults to body length.
|
|
9
|
+
def send_status_no_connection_close(content_length=@body.length)
|
|
10
|
+
unless @status_sent
|
|
11
|
+
write(NO_CLOSE_STATUS_FORMAT % [@status, Mongrel::HTTP_STATUS_CODES[@status]])
|
|
12
|
+
@status_sent = true
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
module Merb
|
|
18
|
+
module Rack
|
|
19
|
+
module Handler
|
|
20
|
+
class Mongrel < ::Mongrel::HttpHandler
|
|
21
|
+
# Runs the server and yields it to a block.
|
|
22
|
+
#
|
|
23
|
+
# ==== Parameters
|
|
24
|
+
# app<Merb::Rack::Application>:: The app that Mongrel should handle.
|
|
25
|
+
# options<Hash>:: Options to pass to Mongrel (see below).
|
|
26
|
+
#
|
|
27
|
+
# ==== Block parameters
|
|
28
|
+
# server<Mongrel::HttpServer>:: The server to run.
|
|
29
|
+
#
|
|
30
|
+
# ==== Options (options)
|
|
31
|
+
# :Host<String>::
|
|
32
|
+
# The hostname on which the app should run. Defaults to "0.0.0.0"
|
|
33
|
+
# :Port<Fixnum>:: The port for the app. Defaults to 8080.
|
|
34
|
+
def self.run(app, options={})
|
|
35
|
+
server = ::Mongrel::HttpServer.new(options[:Host] || '0.0.0.0',
|
|
36
|
+
options[:Port] || 8080)
|
|
37
|
+
server.register('/', ::Merb::Rack::Handler::Mongrel.new(app))
|
|
38
|
+
yield server if block_given?
|
|
39
|
+
server.run.join
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# ==== Parameters
|
|
43
|
+
# app<Merb::Rack::Application>:: The app that Mongrel should handle.
|
|
44
|
+
def initialize(app)
|
|
45
|
+
@app = app
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# ==== Parameters
|
|
49
|
+
# request<Merb::Request>:: The HTTP request to handle.
|
|
50
|
+
# response<HTTPResponse>:: The response object to write response to.
|
|
51
|
+
def process(request, response)
|
|
52
|
+
env = {}.replace(request.params)
|
|
53
|
+
env.delete "HTTP_CONTENT_TYPE"
|
|
54
|
+
env.delete "HTTP_CONTENT_LENGTH"
|
|
55
|
+
|
|
56
|
+
env["SCRIPT_NAME"] = "" if env["SCRIPT_NAME"] == "/"
|
|
57
|
+
|
|
58
|
+
env.update({"rack.version" => [0,1],
|
|
59
|
+
"rack.input" => request.body || StringIO.new(""),
|
|
60
|
+
"rack.errors" => STDERR,
|
|
61
|
+
|
|
62
|
+
"rack.multithread" => true,
|
|
63
|
+
"rack.multiprocess" => false, # ???
|
|
64
|
+
"rack.run_once" => false,
|
|
65
|
+
|
|
66
|
+
"rack.url_scheme" => "http",
|
|
67
|
+
"rack.streaming" => true
|
|
68
|
+
})
|
|
69
|
+
env["QUERY_STRING"] ||= ""
|
|
70
|
+
env.delete "PATH_INFO" if env["PATH_INFO"] == ""
|
|
71
|
+
|
|
72
|
+
status, headers, body = @app.call(env)
|
|
73
|
+
|
|
74
|
+
begin
|
|
75
|
+
response.status = status.to_i
|
|
76
|
+
headers.each { |k, vs|
|
|
77
|
+
vs.each { |v|
|
|
78
|
+
response.header[k] = v
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if Proc === body
|
|
83
|
+
body.call(response)
|
|
84
|
+
else
|
|
85
|
+
body.each { |part|
|
|
86
|
+
response.body << part
|
|
87
|
+
}
|
|
88
|
+
end
|
|
89
|
+
response.finished
|
|
90
|
+
ensure
|
|
91
|
+
body.close if body.respond_to? :close
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
require 'etc'
|
|
2
|
+
module Merb
|
|
3
|
+
|
|
4
|
+
# Server encapsulates the management of Merb daemons.
|
|
5
|
+
class Server
|
|
6
|
+
class << self
|
|
7
|
+
|
|
8
|
+
# Start a Merb server, in either foreground, daemonized or cluster mode.
|
|
9
|
+
#
|
|
10
|
+
# ==== Parameters
|
|
11
|
+
# port<~to_i>::
|
|
12
|
+
# The port to which the first server instance should bind to.
|
|
13
|
+
# Subsequent server instances bind to the immediately following ports.
|
|
14
|
+
# cluster<~to_i>::
|
|
15
|
+
# Number of servers to run in a cluster.
|
|
16
|
+
#
|
|
17
|
+
# ==== Alternatives
|
|
18
|
+
# If cluster is left out, then one process will be started. This process
|
|
19
|
+
# will be daemonized if Merb::Config[:daemonize] is true.
|
|
20
|
+
def start(port, cluster=nil)
|
|
21
|
+
@port = port
|
|
22
|
+
@cluster = cluster
|
|
23
|
+
if @cluster
|
|
24
|
+
@port.to_i.upto(@port.to_i + @cluster.to_i-1) do |port|
|
|
25
|
+
unless alive?(port)
|
|
26
|
+
remove_pid_file(port)
|
|
27
|
+
puts "Starting merb server on port: #{port}"
|
|
28
|
+
daemonize(port)
|
|
29
|
+
else
|
|
30
|
+
raise "Merb is already running on port: #{port}"
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
elsif Merb::Config[:daemonize]
|
|
34
|
+
unless alive?(@port)
|
|
35
|
+
remove_pid_file(@port)
|
|
36
|
+
daemonize(@port)
|
|
37
|
+
else
|
|
38
|
+
raise "Merb is already running on port: #{port}"
|
|
39
|
+
end
|
|
40
|
+
else
|
|
41
|
+
trap('TERM') { exit }
|
|
42
|
+
BootLoader.run
|
|
43
|
+
Merb.adapter.start(Merb::Config.to_hash)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# ==== Parameters
|
|
48
|
+
# port<~to_s>:: The port to check for Merb instances on.
|
|
49
|
+
#
|
|
50
|
+
# ==== Returns
|
|
51
|
+
# Boolean::
|
|
52
|
+
# True if Merb is running on the specified port.
|
|
53
|
+
def alive?(port)
|
|
54
|
+
f = "#{Merb.log_path}" / "merb.#{port}.pid"
|
|
55
|
+
pid = IO.read(f).chomp.to_i
|
|
56
|
+
Process.kill(0, pid)
|
|
57
|
+
true
|
|
58
|
+
rescue
|
|
59
|
+
false
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# ==== Parameters
|
|
63
|
+
# port<~to_s>:: The port of the Merb process to kill.
|
|
64
|
+
# sig<~to_s>:: The signal to send to the process. Defaults to 9.
|
|
65
|
+
#
|
|
66
|
+
# ==== Alternatives
|
|
67
|
+
# If you pass "all" as the port, the signal will be sent to all Merb
|
|
68
|
+
# processes.
|
|
69
|
+
def kill(port, sig=9)
|
|
70
|
+
Merb::BootLoader::BuildFramework.run
|
|
71
|
+
begin
|
|
72
|
+
Dir[Merb.log_path/ "merb.#{port == 'all' ? '*' : port }.pid"].each do |f|
|
|
73
|
+
pid = IO.read(f).chomp.to_i
|
|
74
|
+
begin
|
|
75
|
+
Process.kill(sig, pid)
|
|
76
|
+
FileUtils.rm(f) if File.exist?(f)
|
|
77
|
+
puts "killed PID #{pid} with signal #{sig}"
|
|
78
|
+
rescue Errno::EINVAL
|
|
79
|
+
puts "Failed to kill PID #{pid}: '#{sig}' is an invalid or unsupported signal number."
|
|
80
|
+
rescue Errno::EPERM
|
|
81
|
+
puts "Failed to kill PID #{pid}: Insufficient permissions."
|
|
82
|
+
rescue Errno::ESRCH
|
|
83
|
+
puts "Failed to kill PID #{pid}: Process is deceased or zombie."
|
|
84
|
+
FileUtils.rm f
|
|
85
|
+
rescue Exception => e
|
|
86
|
+
puts "Failed to kill PID #{pid}: #{e.message}"
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
ensure
|
|
90
|
+
exit
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# ==== Parameters
|
|
95
|
+
# port<~to_s>:: The port of the Merb process to daemonize.
|
|
96
|
+
def daemonize(port)
|
|
97
|
+
fork do
|
|
98
|
+
Process.setsid
|
|
99
|
+
exit if fork
|
|
100
|
+
File.umask 0000
|
|
101
|
+
STDIN.reopen "/dev/null"
|
|
102
|
+
STDOUT.reopen "/dev/null", "a"
|
|
103
|
+
STDERR.reopen STDOUT
|
|
104
|
+
trap("TERM") { exit }
|
|
105
|
+
Dir.chdir Merb::Config[:merb_root]
|
|
106
|
+
at_exit { remove_pid_file(port) }
|
|
107
|
+
Merb::Config[:port] = port
|
|
108
|
+
if Merb::Config[:user]
|
|
109
|
+
if Merb::Config[:group]
|
|
110
|
+
change_privilege(Merb::Config[:user], Merb::Config[:group])
|
|
111
|
+
else
|
|
112
|
+
change_privilege(Merb::Config[:user])
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
BootLoader.run
|
|
116
|
+
Merb.adapter.start(Merb::Config.to_hash)
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# Removes a PID file from the filesystem.
|
|
121
|
+
#
|
|
122
|
+
# ==== Parameters
|
|
123
|
+
# port<~to_s>::
|
|
124
|
+
# The port of the Merb process to whom the the PID file belongs to.
|
|
125
|
+
#
|
|
126
|
+
# ==== Alternatives
|
|
127
|
+
# If Merb::Config[:pid_file] has been specified, that will be used
|
|
128
|
+
# instead of the port based PID file.
|
|
129
|
+
def remove_pid_file(port)
|
|
130
|
+
if Merb::Config[:pid_file]
|
|
131
|
+
pidfile = Merb::Config[:pid_file]
|
|
132
|
+
else
|
|
133
|
+
pidfile = Merb.log_path / "merb.#{port}.pid"
|
|
134
|
+
end
|
|
135
|
+
FileUtils.rm(pidfile) if File.exist?(pidfile)
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
# Stores a PID file on the filesystem.
|
|
139
|
+
#
|
|
140
|
+
# ==== Parameters
|
|
141
|
+
# port<~to_s>::
|
|
142
|
+
# The port of the Merb process to whom the the PID file belongs to.
|
|
143
|
+
#
|
|
144
|
+
# ==== Alternatives
|
|
145
|
+
# If Merb::Config[:pid_file] has been specified, that will be used
|
|
146
|
+
# instead of the port based PID file.
|
|
147
|
+
def store_pid(port)
|
|
148
|
+
FileUtils.mkdir_p(Merb.log_path) unless File.directory?(Merb.log_path)
|
|
149
|
+
if Merb::Config[:pid_file]
|
|
150
|
+
pidfile = Merb::Config[:pid_file]
|
|
151
|
+
else
|
|
152
|
+
pidfile = Merb.log_path / "merb.#{port}.pid"
|
|
153
|
+
end
|
|
154
|
+
File.open(pidfile, 'w'){ |f| f.write("#{Process.pid}") }
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
# Change privileges of the process to the specified user and group.
|
|
158
|
+
#
|
|
159
|
+
# ==== Parameters
|
|
160
|
+
# user<String>:: The user who should own the server process.
|
|
161
|
+
# group<String>:: The group who should own the server process.
|
|
162
|
+
#
|
|
163
|
+
# ==== Alternatives
|
|
164
|
+
# If group is left out, the user will be used as the group.
|
|
165
|
+
def change_privilege(user, group=user)
|
|
166
|
+
|
|
167
|
+
puts "Changing privileges to #{user}:#{group}"
|
|
168
|
+
|
|
169
|
+
uid, gid = Process.euid, Process.egid
|
|
170
|
+
target_uid = Etc.getpwnam(user).uid
|
|
171
|
+
target_gid = Etc.getgrnam(group).gid
|
|
172
|
+
|
|
173
|
+
if uid != target_uid || gid != target_gid
|
|
174
|
+
# Change process ownership
|
|
175
|
+
Process.initgroups(user, target_gid)
|
|
176
|
+
Process::GID.change_privilege(target_gid)
|
|
177
|
+
Process::UID.change_privilege(target_uid)
|
|
178
|
+
end
|
|
179
|
+
rescue Errno::EPERM => e
|
|
180
|
+
puts "Couldn't change user and group to #{user}:#{group}: #{e}"
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
end
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# This is a place holder to allow plugins etc a place to include
|
|
2
|
+
# testing helpers
|
|
3
|
+
module Merb::Test::Helpers; end
|
|
4
|
+
|
|
5
|
+
require "merb-core/test/helpers/request_helper"
|
|
6
|
+
require "merb-core/test/helpers/multipart_request_helper"
|
|
7
|
+
require "merb-core/test/helpers/controller_helper"
|
|
8
|
+
require "merb-core/test/helpers/route_helper"
|
|
9
|
+
require "merb-core/test/helpers/view_helper"
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
module Merb::Test::MultipartRequestHelper
|
|
2
|
+
require 'rubygems'
|
|
3
|
+
require 'mime/types'
|
|
4
|
+
|
|
5
|
+
class Param
|
|
6
|
+
attr_accessor :key, :value
|
|
7
|
+
|
|
8
|
+
# ==== Parameters
|
|
9
|
+
# key<~to_s>:: The parameter key.
|
|
10
|
+
# value<~to_s>:: The parameter value.
|
|
11
|
+
def initialize(key, value)
|
|
12
|
+
@key = key
|
|
13
|
+
@value = value
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# ==== Returns
|
|
17
|
+
# String:: The parameter in a form suitable for a multipart request.
|
|
18
|
+
def to_multipart
|
|
19
|
+
return %(Content-Disposition: form-data; name="#{key}"\r\n\r\n#{value}\r\n)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
class FileParam
|
|
24
|
+
attr_accessor :key, :filename, :content
|
|
25
|
+
|
|
26
|
+
# ==== Parameters
|
|
27
|
+
# key<~to_s>:: The parameter key.
|
|
28
|
+
# filename<~to_s>:: Name of the file for this parameter.
|
|
29
|
+
# content<~to_s>:: Content of the file for this parameter.
|
|
30
|
+
def initialize(key, filename, content)
|
|
31
|
+
@key = key
|
|
32
|
+
@filename = filename
|
|
33
|
+
@content = content
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# ==== Returns
|
|
37
|
+
# String::
|
|
38
|
+
# The file parameter in a form suitable for a multipart request.
|
|
39
|
+
def to_multipart
|
|
40
|
+
return %(Content-Disposition: form-data; name="#{key}"; filename="#{filename}"\r\n) + "Content-Type: #{MIME::Types.type_for(@filename)}\r\n\r\n" + content + "\r\n"
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
class Post
|
|
45
|
+
BOUNDARY = '----------0xKhTmLbOuNdArY'
|
|
46
|
+
CONTENT_TYPE = "multipart/form-data, boundary=" + BOUNDARY
|
|
47
|
+
|
|
48
|
+
# ==== Parameters
|
|
49
|
+
# params<Hash>:: Optional params for the controller.
|
|
50
|
+
def initialize(params = {})
|
|
51
|
+
@multipart_params = []
|
|
52
|
+
push_params(params)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Saves the params in an array of multipart params as Param and
|
|
56
|
+
# FileParam objects.
|
|
57
|
+
#
|
|
58
|
+
# ==== Parameters
|
|
59
|
+
# params<Hash>:: The params to add to the multipart params.
|
|
60
|
+
# prefix<~to_s>:: An optional prefix for the request string keys.
|
|
61
|
+
def push_params(params, prefix = nil)
|
|
62
|
+
params.sort_by {|k| k.to_s}.each do |key, value|
|
|
63
|
+
param_key = prefix.nil? ? key : "#{prefix}[#{key}]"
|
|
64
|
+
if value.respond_to?(:read)
|
|
65
|
+
@multipart_params << FileParam.new(param_key, value.path, value.read)
|
|
66
|
+
else
|
|
67
|
+
if value.is_a?(Hash) || value.is_a?(Mash)
|
|
68
|
+
value.keys.each do |k|
|
|
69
|
+
push_params(value, param_key)
|
|
70
|
+
end
|
|
71
|
+
else
|
|
72
|
+
@multipart_params << Param.new(param_key, value)
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# ==== Returns
|
|
79
|
+
# Array[String, String]:: The query and the content type.
|
|
80
|
+
def to_multipart
|
|
81
|
+
query = @multipart_params.collect { |param| "--" + BOUNDARY + "\r\n" + param.to_multipart }.join("") + "--" + BOUNDARY + "--"
|
|
82
|
+
return query, CONTENT_TYPE
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Similar to dispatch_to but allows for sending files inside params.
|
|
87
|
+
#
|
|
88
|
+
# ==== Paramters
|
|
89
|
+
# controller_klass<Controller>::
|
|
90
|
+
# The controller class object that the action should be dispatched to.
|
|
91
|
+
# action<Symbol>:: The action name, as a symbol.
|
|
92
|
+
# params<Hash>::
|
|
93
|
+
# An optional hash that will end up as params in the controller instance.
|
|
94
|
+
# env<Hash>::
|
|
95
|
+
# An optional hash that is passed to the fake request. Any request options
|
|
96
|
+
# should go here (see +fake_request+).
|
|
97
|
+
# &blk:: The block is executed in the context of the controller.
|
|
98
|
+
#
|
|
99
|
+
# ==== Example
|
|
100
|
+
# dispatch_multipart_to(MyController, :create, :my_file => @a_file ) do
|
|
101
|
+
# self.stub!(:current_user).and_return(@user)
|
|
102
|
+
# end
|
|
103
|
+
#
|
|
104
|
+
# ==== Note
|
|
105
|
+
# Set your option to contain a file object to simulate file uploads.
|
|
106
|
+
#
|
|
107
|
+
# Does not use routes.
|
|
108
|
+
#---
|
|
109
|
+
# @public
|
|
110
|
+
def dispatch_multipart_to(controller_klass, action, params = {}, env = {}, &blk)
|
|
111
|
+
request = multipart_fake_request(env, params)
|
|
112
|
+
dispatch_request(request, controller_klass, action, &blk)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# An HTTP POST request that operates through the router and uses multipart
|
|
116
|
+
# parameters.
|
|
117
|
+
#
|
|
118
|
+
# ==== Parameters
|
|
119
|
+
# path<String>:: The path that should go to the router as the request uri.
|
|
120
|
+
# params<Hash>::
|
|
121
|
+
# An optional hash that will end up as params in the controller instance.
|
|
122
|
+
# env<Hash>::
|
|
123
|
+
# An optional hash that is passed to the fake request. Any request options
|
|
124
|
+
# should go here (see +fake_request+).
|
|
125
|
+
# block<Proc>:: The block is executed in the context of the controller.
|
|
126
|
+
#
|
|
127
|
+
# ==== Note
|
|
128
|
+
# To include an uploaded file, put a file object as a value in params.
|
|
129
|
+
def multipart_post(path, params = {}, env = {}, &block)
|
|
130
|
+
env[:request_method] = "POST"
|
|
131
|
+
env[:test_with_multipart] = true
|
|
132
|
+
request(path, params, env, &block)
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
# An HTTP PUT request that operates through the router and uses multipart
|
|
136
|
+
# parameters.
|
|
137
|
+
#
|
|
138
|
+
# ==== Parameters
|
|
139
|
+
# path<String>:: The path that should go to the router as the request uri.
|
|
140
|
+
# params<Hash>::
|
|
141
|
+
# An optional hash that will end up as params in the controller instance.
|
|
142
|
+
# env<Hash>::
|
|
143
|
+
# An optional hash that is passed to the fake request. Any request options
|
|
144
|
+
# should go here (see +fake_request+).
|
|
145
|
+
# block<Proc>:: The block is executed in the context of the controller.
|
|
146
|
+
#
|
|
147
|
+
# ==== Note
|
|
148
|
+
# To include an uplaoded file, put a file object as a value in params.
|
|
149
|
+
def multipart_put(path, params = {}, env = {}, &block)
|
|
150
|
+
env[:request_method] = "PUT"
|
|
151
|
+
env[:test_with_multipart] = true
|
|
152
|
+
request(path, params, env, &block)
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# ==== Parameters
|
|
156
|
+
# env<Hash>::
|
|
157
|
+
# An optional hash that is passed to the fake request. Any request options
|
|
158
|
+
# should go here (see +fake_request+).
|
|
159
|
+
# params<Hash>::
|
|
160
|
+
# An optional hash that will end up as params in the controller instance.
|
|
161
|
+
#
|
|
162
|
+
# ==== Returns
|
|
163
|
+
# FakeRequest::
|
|
164
|
+
# A multipart Request object that is built based on the parameters.
|
|
165
|
+
def multipart_fake_request(env = {}, params = {})
|
|
166
|
+
if params.empty?
|
|
167
|
+
fake_request(env)
|
|
168
|
+
else
|
|
169
|
+
m = Post.new(params)
|
|
170
|
+
body, head = m.to_multipart
|
|
171
|
+
fake_request(env.merge( :content_type => head,
|
|
172
|
+
:content_length => body.length), :post_body => body)
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
end
|