joe-merb-core 0.9.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (98) hide show
  1. data/CHANGELOG +992 -0
  2. data/CONTRIBUTORS +94 -0
  3. data/LICENSE +20 -0
  4. data/PUBLIC_CHANGELOG +142 -0
  5. data/README +21 -0
  6. data/Rakefile +456 -0
  7. data/TODO +0 -0
  8. data/bin/merb +11 -0
  9. data/bin/merb-specs +5 -0
  10. data/lib/merb-core.rb +648 -0
  11. data/lib/merb-core/autoload.rb +31 -0
  12. data/lib/merb-core/bootloader.rb +889 -0
  13. data/lib/merb-core/config.rb +380 -0
  14. data/lib/merb-core/constants.rb +45 -0
  15. data/lib/merb-core/controller/abstract_controller.rb +620 -0
  16. data/lib/merb-core/controller/exceptions.rb +302 -0
  17. data/lib/merb-core/controller/merb_controller.rb +283 -0
  18. data/lib/merb-core/controller/mime.rb +111 -0
  19. data/lib/merb-core/controller/mixins/authentication.rb +123 -0
  20. data/lib/merb-core/controller/mixins/conditional_get.rb +83 -0
  21. data/lib/merb-core/controller/mixins/controller.rb +316 -0
  22. data/lib/merb-core/controller/mixins/render.rb +513 -0
  23. data/lib/merb-core/controller/mixins/responder.rb +469 -0
  24. data/lib/merb-core/controller/template.rb +254 -0
  25. data/lib/merb-core/core_ext.rb +9 -0
  26. data/lib/merb-core/core_ext/hash.rb +7 -0
  27. data/lib/merb-core/core_ext/kernel.rb +345 -0
  28. data/lib/merb-core/dispatch/cookies.rb +130 -0
  29. data/lib/merb-core/dispatch/default_exception/default_exception.rb +93 -0
  30. data/lib/merb-core/dispatch/default_exception/views/_css.html.erb +200 -0
  31. data/lib/merb-core/dispatch/default_exception/views/_javascript.html.erb +77 -0
  32. data/lib/merb-core/dispatch/default_exception/views/index.html.erb +98 -0
  33. data/lib/merb-core/dispatch/dispatcher.rb +172 -0
  34. data/lib/merb-core/dispatch/request.rb +718 -0
  35. data/lib/merb-core/dispatch/router.rb +228 -0
  36. data/lib/merb-core/dispatch/router/behavior.rb +610 -0
  37. data/lib/merb-core/dispatch/router/cached_proc.rb +52 -0
  38. data/lib/merb-core/dispatch/router/resources.rb +220 -0
  39. data/lib/merb-core/dispatch/router/route.rb +560 -0
  40. data/lib/merb-core/dispatch/session.rb +222 -0
  41. data/lib/merb-core/dispatch/session/container.rb +74 -0
  42. data/lib/merb-core/dispatch/session/cookie.rb +173 -0
  43. data/lib/merb-core/dispatch/session/memcached.rb +68 -0
  44. data/lib/merb-core/dispatch/session/memory.rb +99 -0
  45. data/lib/merb-core/dispatch/session/store_container.rb +150 -0
  46. data/lib/merb-core/dispatch/worker.rb +28 -0
  47. data/lib/merb-core/gem_ext/erubis.rb +77 -0
  48. data/lib/merb-core/logger.rb +215 -0
  49. data/lib/merb-core/plugins.rb +67 -0
  50. data/lib/merb-core/rack.rb +27 -0
  51. data/lib/merb-core/rack/adapter.rb +47 -0
  52. data/lib/merb-core/rack/adapter/ebb.rb +24 -0
  53. data/lib/merb-core/rack/adapter/evented_mongrel.rb +13 -0
  54. data/lib/merb-core/rack/adapter/fcgi.rb +17 -0
  55. data/lib/merb-core/rack/adapter/irb.rb +119 -0
  56. data/lib/merb-core/rack/adapter/mongrel.rb +33 -0
  57. data/lib/merb-core/rack/adapter/runner.rb +28 -0
  58. data/lib/merb-core/rack/adapter/swiftiplied_mongrel.rb +14 -0
  59. data/lib/merb-core/rack/adapter/thin.rb +40 -0
  60. data/lib/merb-core/rack/adapter/thin_turbo.rb +17 -0
  61. data/lib/merb-core/rack/adapter/webrick.rb +72 -0
  62. data/lib/merb-core/rack/application.rb +32 -0
  63. data/lib/merb-core/rack/handler/mongrel.rb +96 -0
  64. data/lib/merb-core/rack/middleware.rb +20 -0
  65. data/lib/merb-core/rack/middleware/conditional_get.rb +29 -0
  66. data/lib/merb-core/rack/middleware/content_length.rb +18 -0
  67. data/lib/merb-core/rack/middleware/csrf.rb +73 -0
  68. data/lib/merb-core/rack/middleware/path_prefix.rb +31 -0
  69. data/lib/merb-core/rack/middleware/profiler.rb +19 -0
  70. data/lib/merb-core/rack/middleware/static.rb +45 -0
  71. data/lib/merb-core/rack/middleware/tracer.rb +20 -0
  72. data/lib/merb-core/server.rb +321 -0
  73. data/lib/merb-core/tasks/audit.rake +68 -0
  74. data/lib/merb-core/tasks/gem_management.rb +252 -0
  75. data/lib/merb-core/tasks/merb.rb +2 -0
  76. data/lib/merb-core/tasks/merb_rake_helper.rb +51 -0
  77. data/lib/merb-core/tasks/stats.rake +71 -0
  78. data/lib/merb-core/test.rb +17 -0
  79. data/lib/merb-core/test/helpers.rb +10 -0
  80. data/lib/merb-core/test/helpers/controller_helper.rb +8 -0
  81. data/lib/merb-core/test/helpers/multipart_request_helper.rb +176 -0
  82. data/lib/merb-core/test/helpers/request_helper.rb +61 -0
  83. data/lib/merb-core/test/helpers/route_helper.rb +47 -0
  84. data/lib/merb-core/test/helpers/view_helper.rb +121 -0
  85. data/lib/merb-core/test/matchers.rb +10 -0
  86. data/lib/merb-core/test/matchers/controller_matchers.rb +108 -0
  87. data/lib/merb-core/test/matchers/route_matchers.rb +137 -0
  88. data/lib/merb-core/test/matchers/view_matchers.rb +393 -0
  89. data/lib/merb-core/test/run_specs.rb +141 -0
  90. data/lib/merb-core/test/tasks/spectasks.rb +68 -0
  91. data/lib/merb-core/test/test_ext/hpricot.rb +32 -0
  92. data/lib/merb-core/test/test_ext/object.rb +14 -0
  93. data/lib/merb-core/test/test_ext/string.rb +14 -0
  94. data/lib/merb-core/vendor/facets.rb +2 -0
  95. data/lib/merb-core/vendor/facets/dictionary.rb +433 -0
  96. data/lib/merb-core/vendor/facets/inflect.rb +342 -0
  97. data/lib/merb-core/version.rb +3 -0
  98. metadata +253 -0
@@ -0,0 +1,17 @@
1
+ require "thin-turbo"
2
+
3
+ module Merb
4
+
5
+ module Rack
6
+
7
+ class ThinTurbo < Thin
8
+ # start a Thin Turbo server on given host and port.
9
+
10
+ def self.new_server(port)
11
+ @opts.merge!(:backend => ::Thin::Backends::Turbo)
12
+ super
13
+ end
14
+
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,72 @@
1
+ require 'webrick'
2
+ require 'webrick/utils'
3
+ require 'rack/handler/webrick'
4
+ module Merb
5
+ module Rack
6
+
7
+ class WEBrick < Merb::Rack::AbstractAdapter
8
+
9
+ class << self
10
+ attr_accessor :server
11
+ end
12
+
13
+ def self.new_server(port)
14
+ options = {
15
+ :Port => port,
16
+ :BindAddress => @opts[:host],
17
+ :Logger => Merb.logger,
18
+ :AccessLog => [
19
+ [Merb.logger, ::WEBrick::AccessLog::COMMON_LOG_FORMAT],
20
+ [Merb.logger, ::WEBrick::AccessLog::REFERER_LOG_FORMAT]
21
+ ]
22
+ }
23
+
24
+ sockets = ::WEBrick::Utils.create_listeners nil, port
25
+ @server = ::WEBrick::HTTPServer.new(options.merge(:DoNotListen => true))
26
+ @server.listeners.replace sockets
27
+ end
28
+
29
+ def self.start_server
30
+ @server.mount("/", ::Rack::Handler::WEBrick, @opts[:app])
31
+ @server.start
32
+ exit(@status)
33
+ end
34
+
35
+ def self.stop(status = 0)
36
+ @status = status
37
+ @server.shutdown
38
+ end
39
+
40
+ def self.exit_process(status = 0)
41
+ end
42
+
43
+ # start WEBrick server on given host and port.
44
+
45
+ # ==== Parameters
46
+ # opts<Hash>:: Options for WEBrick (see below).
47
+ #
48
+ # ==== Options (opts)
49
+ # :host<String>:: The hostname that WEBrick should serve.
50
+ # :port<Fixnum>:: The port WEBrick should bind to.
51
+ # :app<String>>:: The application name.
52
+ # def self.start(opts={})
53
+ # Merb.logger.warn!("Using Webrick adapter")
54
+ #
55
+ # options = {
56
+ # :Port => opts[:port],
57
+ # :BindAddress => opts[:host],
58
+ # :Logger => Merb.logger,
59
+ # :AccessLog => [
60
+ # [Merb.logger, ::WEBrick::AccessLog::COMMON_LOG_FORMAT],
61
+ # [Merb.logger, ::WEBrick::AccessLog::REFERER_LOG_FORMAT]
62
+ # ]
63
+ # }
64
+ #
65
+ # server = ::WEBrick::HTTPServer.new(options)
66
+ # Merb::Server.change_privilege
67
+ # server.mount("/", ::Rack::Handler::WEBrick, opts[:app])
68
+ # server.start
69
+ # end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,32 @@
1
+ module Merb
2
+ module Rack
3
+ class Application
4
+
5
+ def call(env)
6
+ begin
7
+ controller = ::Merb::Dispatcher.handle(Merb::Request.new(env))
8
+ rescue Object => e
9
+ return [500, {Merb::Const::CONTENT_TYPE => "text/html"}, e.message + "<br/>" + e.backtrace.join("<br/>")]
10
+ end
11
+ Merb.logger.info "\n\n"
12
+ Merb.logger.flush
13
+
14
+ # unless controller.headers[Merb::Const::DATE]
15
+ # require "time"
16
+ # controller.headers[Merb::Const::DATE] = Time.now.rfc2822.to_s
17
+ # end
18
+ controller.rack_response
19
+ end
20
+
21
+ def deferred?(env)
22
+ path = env[Merb::Const::PATH_INFO] ? env[Merb::Const::PATH_INFO].chomp('/') : ""
23
+ if path =~ Merb.deferred_actions
24
+ Merb.logger.info! "Deferring Request: #{path}"
25
+ true
26
+ else
27
+ false
28
+ end
29
+ end # deferred?(env)
30
+ end # Application
31
+ end # Rack
32
+ end # Merb
@@ -0,0 +1,96 @@
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
+ def self.stop(block = true)
43
+ @server.stop
44
+ end
45
+
46
+ # ==== Parameters
47
+ # app<Merb::Rack::Application>:: The app that Mongrel should handle.
48
+ def initialize(app)
49
+ @app = app
50
+ end
51
+
52
+ # ==== Parameters
53
+ # request<Merb::Request>:: The HTTP request to handle.
54
+ # response<HTTPResponse>:: The response object to write response to.
55
+ def process(request, response)
56
+ env = {}.replace(request.params)
57
+ env.delete "HTTP_CONTENT_TYPE"
58
+ env.delete "HTTP_CONTENT_LENGTH"
59
+
60
+ env["SCRIPT_NAME"] = "" if env["SCRIPT_NAME"] == "/"
61
+
62
+ env.update({"rack.version" => [0,1],
63
+ "rack.input" => request.body || StringIO.new(""),
64
+ "rack.errors" => STDERR,
65
+
66
+ "rack.multithread" => true,
67
+ "rack.multiprocess" => false, # ???
68
+ "rack.run_once" => false,
69
+
70
+ "rack.url_scheme" => "http"
71
+ })
72
+ env["QUERY_STRING"] ||= ""
73
+ env.delete "PATH_INFO" if env["PATH_INFO"] == ""
74
+
75
+ status, headers, body = @app.call(env)
76
+
77
+ begin
78
+ response.status = status.to_i
79
+ headers.each { |k, vs|
80
+ vs.each { |v|
81
+ response.header[k] = v
82
+ }
83
+ }
84
+
85
+ body.each { |part|
86
+ response.body << part
87
+ }
88
+ response.finished
89
+ ensure
90
+ body.close if body.respond_to? :close
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,20 @@
1
+ module Merb
2
+ module Rack
3
+ class Middleware
4
+
5
+ def initialize(app)
6
+ @app = app
7
+ end
8
+
9
+ def deferred?(env)
10
+ @app.deferred?(env)
11
+ end
12
+
13
+ def call(env)
14
+ @app.call(env)
15
+ end
16
+
17
+ end
18
+ end
19
+ end
20
+
@@ -0,0 +1,29 @@
1
+ module Merb
2
+ module Rack
3
+
4
+ class ConditionalGet < Merb::Rack::Middleware
5
+ def call(env)
6
+ status, headers, body = @app.call(env)
7
+
8
+ if document_not_modified?(env, headers)
9
+ status = 304
10
+ body = ""
11
+ # set Date header using RFC1123 date format as specified by HTTP
12
+ # RFC2616 section 3.3.1.
13
+ end
14
+
15
+ [status, headers, body]
16
+ end
17
+
18
+ private
19
+ def document_not_modified?(env, headers)
20
+ if etag = headers[Merb::Const::ETAG]
21
+ etag == env[Merb::Const::HTTP_IF_NONE_MATCH]
22
+ elsif last_modified = headers[Merb::Const::LAST_MODIFIED]
23
+ last_modified == env[Merb::Const::HTTP_IF_MODIFIED_SINCE]
24
+ end
25
+ end
26
+ end
27
+
28
+ end
29
+ end
@@ -0,0 +1,18 @@
1
+ module Merb
2
+ module Rack
3
+
4
+ class ContentLength < Merb::Rack::Middleware
5
+ def call(env)
6
+ status, headers, body = @app.call(env)
7
+
8
+ # to_s is because Rack spec expects header
9
+ # values to be iterable and yield strings
10
+ header = 'Content-Length'.freeze
11
+ headers[header] = body.size.to_s unless headers.has_key?(header)
12
+
13
+ [status, headers, body]
14
+ end
15
+ end
16
+
17
+ end
18
+ end
@@ -0,0 +1,73 @@
1
+ require 'digest/md5'
2
+
3
+ module Merb
4
+ module Rack
5
+
6
+ class Csrf < Merb::Rack::Middleware
7
+ HTML_TYPES = %w(text/html application/xhtml+xml)
8
+ POST_FORM_RE = Regexp.compile('(<form\W[^>]*\bmethod=(\'|"|)POST(\'|"|)\b[^>]*>)', Regexp::IGNORECASE)
9
+ ERROR_MSG = '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"><body><h1>403 Forbidden</h1><p>Cross Site Request Forgery detected. Request aborted.</p></body></html>'.freeze
10
+
11
+ def call(env)
12
+ status, header, body = @app.call(env)
13
+ body = body.to_s
14
+ if env[Merb::Const::REQUEST_METHOD] == Merb::Const::GET
15
+ body = process_response(body) if valid_content_type?(header[Merb::Const::CONTENT_TYPE])
16
+ elsif env[Merb::Const::REQUEST_METHOD] == Merb::Const::POST
17
+ status, body = process_request(env, status, body)
18
+ end
19
+
20
+ [status, header, body]
21
+ end
22
+
23
+ private
24
+ def process_request(env, status, body)
25
+ session_id = Merb::Config[:session_id_key]
26
+ csrf_token = _make_token(session_id)
27
+
28
+ request_csrf_token = env['csrf_authentication_token']
29
+
30
+ unless csrf_token == request_csrf_token
31
+ exception = Merb::ControllerExceptions::Forbidden.new(ERROR_MSG)
32
+ status = exception.status
33
+ body = exception.message
34
+
35
+ return [status, body]
36
+ end
37
+
38
+ return [status, body]
39
+ end
40
+
41
+ def process_response(body)
42
+ session_id = Merb::Config[:session_id_key]
43
+ csrf_token = _make_token(session_id)
44
+
45
+ if csrf_token
46
+ modified_body = ''
47
+ body.scan(POST_FORM_RE) do |match|
48
+ modified_body << add_csrf_field($~, csrf_token)
49
+ end
50
+
51
+ body = modified_body
52
+ end
53
+
54
+ body
55
+ end
56
+
57
+ def add_csrf_field(match, csrf_token)
58
+ modified_body = match.pre_match
59
+ modified_body << match.to_s
60
+ modified_body << "<div style='display: none;'><input type='hidden' id='csrf_authentication_token' name='csrf_authentication_token' value='#{csrf_token}' /></div>"
61
+ modified_body << match.post_match
62
+ end
63
+
64
+ def valid_content_type?(content_type)
65
+ HTML_TYPES.include?(content_type.split(';').first)
66
+ end
67
+
68
+ def _make_token(session_id)
69
+ Digest::MD5.hexdigest(Merb::Config[:session_secret_key] + session_id)
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,31 @@
1
+ module Merb
2
+ module Rack
3
+ class PathPrefix < Merb::Rack::Middleware
4
+
5
+ def initialize(app, path_prefix = nil)
6
+ super(app)
7
+ @path_prefix = /^#{Regexp.escape(path_prefix)}/
8
+ end
9
+
10
+ def deferred?(env)
11
+ strip_path_prefix(env)
12
+ @app.deferred?(env)
13
+ end
14
+
15
+ def call(env)
16
+ strip_path_prefix(env)
17
+ @app.call(env)
18
+ end
19
+
20
+ def strip_path_prefix(env)
21
+ ['PATH_INFO', 'REQUEST_URI'].each do |path_key|
22
+ if env[path_key] =~ @path_prefix
23
+ env[path_key].sub!(@path_prefix, '')
24
+ env[path_key] = '/' if env[path_key].empty?
25
+ end
26
+ end
27
+ end
28
+
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,19 @@
1
+ module Merb
2
+ module Rack
3
+ class Profiler < Merb::Rack::Middleware
4
+
5
+ def initialize(app, min=1, iter=1)
6
+ super(app)
7
+ @min, @iter = min, iter
8
+ end
9
+
10
+ def call(env)
11
+ __profile__("profile_output", @min, @iter) do
12
+ @app.call(env)
13
+ end
14
+ end
15
+
16
+
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,45 @@
1
+ module Merb
2
+ module Rack
3
+ class Static < Merb::Rack::Middleware
4
+
5
+ def initialize(app,directory)
6
+ super(app)
7
+ @static_server = ::Rack::File.new(directory)
8
+ end
9
+
10
+ def call(env)
11
+ path = env['PATH_INFO'] ? env['PATH_INFO'].chomp('/') : ""
12
+ cached_path = (path.empty? ? 'index' : path) + '.html'
13
+
14
+ if file_exist?(path) && env['REQUEST_METHOD'] =~ /GET|HEAD/ # Serve the file if it's there and the request method is GET or HEAD
15
+ serve_static(env)
16
+ elsif file_exist?(cached_path) && env['REQUEST_METHOD'] =~ /GET|HEAD/ # Serve the page cache if it's there and the request method is GET or HEAD
17
+ env['PATH_INFO'] = cached_path
18
+ serve_static(env)
19
+ elsif path =~ /favicon\.ico/
20
+ return [404, {"Content-Type"=>"text/html"}, "404 Not Found."]
21
+ else
22
+ @app.call(env)
23
+ end
24
+ end
25
+
26
+ # ==== Parameters
27
+ # path<String>:: The path to the file relative to the server root.
28
+ #
29
+ # ==== Returns
30
+ # Boolean:: True if file exists under the server root and is readable.
31
+ def file_exist?(path)
32
+ full_path = ::File.join(@static_server.root, ::Merb::Request.unescape(path))
33
+ ::File.file?(full_path) && ::File.readable?(full_path)
34
+ end
35
+
36
+ # ==== Parameters
37
+ # env<Hash>:: Environment variables to pass on to the server.
38
+ def serve_static(env)
39
+ env["PATH_INFO"] = ::Merb::Request.unescape(env["PATH_INFO"])
40
+ @static_server.call(env)
41
+ end
42
+
43
+ end
44
+ end
45
+ end