wycats-merb-core 0.9.8

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.
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 +458 -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 +598 -0
  11. data/lib/merb-core/autoload.rb +31 -0
  12. data/lib/merb-core/bootloader.rb +717 -0
  13. data/lib/merb-core/config.rb +305 -0
  14. data/lib/merb-core/constants.rb +45 -0
  15. data/lib/merb-core/controller/abstract_controller.rb +568 -0
  16. data/lib/merb-core/controller/exceptions.rb +315 -0
  17. data/lib/merb-core/controller/merb_controller.rb +256 -0
  18. data/lib/merb-core/controller/mime.rb +107 -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 +319 -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 +340 -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 +198 -0
  31. data/lib/merb-core/dispatch/default_exception/views/_javascript.html.erb +73 -0
  32. data/lib/merb-core/dispatch/default_exception/views/index.html.erb +94 -0
  33. data/lib/merb-core/dispatch/dispatcher.rb +176 -0
  34. data/lib/merb-core/dispatch/request.rb +729 -0
  35. data/lib/merb-core/dispatch/router.rb +151 -0
  36. data/lib/merb-core/dispatch/router/behavior.rb +566 -0
  37. data/lib/merb-core/dispatch/router/cached_proc.rb +52 -0
  38. data/lib/merb-core/dispatch/router/resources.rb +191 -0
  39. data/lib/merb-core/dispatch/router/route.rb +511 -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 +203 -0
  49. data/lib/merb-core/plugins.rb +67 -0
  50. data/lib/merb-core/rack.rb +25 -0
  51. data/lib/merb-core/rack/adapter.rb +44 -0
  52. data/lib/merb-core/rack/adapter/ebb.rb +25 -0
  53. data/lib/merb-core/rack/adapter/evented_mongrel.rb +26 -0
  54. data/lib/merb-core/rack/adapter/fcgi.rb +17 -0
  55. data/lib/merb-core/rack/adapter/irb.rb +118 -0
  56. data/lib/merb-core/rack/adapter/mongrel.rb +26 -0
  57. data/lib/merb-core/rack/adapter/runner.rb +28 -0
  58. data/lib/merb-core/rack/adapter/swiftiplied_mongrel.rb +26 -0
  59. data/lib/merb-core/rack/adapter/thin.rb +39 -0
  60. data/lib/merb-core/rack/adapter/thin_turbo.rb +24 -0
  61. data/lib/merb-core/rack/adapter/webrick.rb +36 -0
  62. data/lib/merb-core/rack/application.rb +32 -0
  63. data/lib/merb-core/rack/handler/mongrel.rb +97 -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 +284 -0
  73. data/lib/merb-core/tasks/audit.rake +68 -0
  74. data/lib/merb-core/tasks/gem_management.rb +229 -0
  75. data/lib/merb-core/tasks/merb.rb +1 -0
  76. data/lib/merb-core/tasks/merb_rake_helper.rb +80 -0
  77. data/lib/merb-core/tasks/stats.rake +71 -0
  78. data/lib/merb-core/test.rb +11 -0
  79. data/lib/merb-core/test/helpers.rb +9 -0
  80. data/lib/merb-core/test/helpers/controller_helper.rb +8 -0
  81. data/lib/merb-core/test/helpers/multipart_request_helper.rb +175 -0
  82. data/lib/merb-core/test/helpers/request_helper.rb +393 -0
  83. data/lib/merb-core/test/helpers/route_helper.rb +39 -0
  84. data/lib/merb-core/test/helpers/view_helper.rb +121 -0
  85. data/lib/merb-core/test/matchers.rb +9 -0
  86. data/lib/merb-core/test/matchers/controller_matchers.rb +351 -0
  87. data/lib/merb-core/test/matchers/route_matchers.rb +137 -0
  88. data/lib/merb-core/test/matchers/view_matchers.rb +375 -0
  89. data/lib/merb-core/test/run_specs.rb +49 -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,24 @@
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
+ # ==== Parameters
11
+ # opts<Hash>:: Options for Thin Turbo (see below).
12
+ #
13
+ # ==== Options (opts)
14
+ # :host<String>:: The hostname that Thin Turbo should serve.
15
+ # :port<Fixnum>:: The port Thin Turbo should bind to.
16
+ # :socket<Fixnum>>:: The socket number that thin should bind to.
17
+ # :socket_file<String>>:: The socket file that thin should attach to.
18
+ # :app<String>>:: The application name.
19
+ def self.start(opts={})
20
+ super(opts.merge(:backend => ::Thin::Backends::Turbo))
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,36 @@
1
+ require 'webrick'
2
+ require 'rack/handler/webrick'
3
+ module Merb
4
+ module Rack
5
+
6
+ class WEBrick
7
+ # start WEBrick server on given host and port.
8
+
9
+ # ==== Parameters
10
+ # opts<Hash>:: Options for WEBrick (see below).
11
+ #
12
+ # ==== Options (opts)
13
+ # :host<String>:: The hostname that WEBrick should serve.
14
+ # :port<Fixnum>:: The port WEBrick should bind to.
15
+ # :app<String>>:: The application name.
16
+ def self.start(opts={})
17
+ Merb.logger.warn!("Using Webrick adapter")
18
+
19
+ options = {
20
+ :Port => opts[:port],
21
+ :BindAddress => opts[:host],
22
+ :Logger => Merb.logger,
23
+ :AccessLog => [
24
+ [Merb.logger, ::WEBrick::AccessLog::COMMON_LOG_FORMAT],
25
+ [Merb.logger, ::WEBrick::AccessLog::REFERER_LOG_FORMAT]
26
+ ]
27
+ }
28
+
29
+ server = ::WEBrick::HTTPServer.new(options)
30
+ Merb::Server.change_privilege
31
+ server.mount("/", ::Rack::Handler::WEBrick, opts[:app])
32
+ server.start
33
+ end
34
+ end
35
+ end
36
+ 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,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,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
+
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
@@ -0,0 +1,20 @@
1
+ module Merb
2
+ module Rack
3
+ class Tracer < Merb::Rack::Middleware
4
+
5
+ def call(env)
6
+
7
+ Merb.logger.debug!("Rack environment:\n" + env.inspect + "\n\n")
8
+
9
+ status, headers, body = @app.call(env)
10
+
11
+ Merb.logger.debug!("Status: #{status.inspect}")
12
+ Merb.logger.debug!("Headers: #{headers.inspect}")
13
+ Merb.logger.debug!("Body: #{body.inspect}")
14
+
15
+ [status, headers, body]
16
+ end
17
+
18
+ end
19
+ end
20
+ end