merb 0.0.7 → 0.0.8
Sign up to get free protection for your applications and to get access to all the features.
- data/README +66 -31
- data/Rakefile +3 -1
- data/bin/merb +47 -13
- data/examples/app_skeleton/Rakefile +4 -3
- data/examples/app_skeleton/dist/app/helpers/global_helper.rb +6 -0
- data/examples/app_skeleton/dist/conf/merb.yml +11 -0
- data/examples/app_skeleton/dist/conf/mup.conf +5 -0
- data/examples/app_skeleton/dist/conf/router.rb +1 -3
- data/examples/app_skeleton/scripts/merb_stop +10 -2
- data/examples/sample_app/Rakefile +3 -3
- data/examples/sample_app/dist/app/controllers/files.rb +3 -3
- data/examples/sample_app/dist/app/controllers/posts.rb +25 -23
- data/examples/sample_app/dist/app/controllers/test.rb +7 -3
- data/examples/sample_app/dist/app/helpers/global_helper.rb +7 -0
- data/examples/sample_app/dist/app/helpers/posts_helper.rb +4 -0
- data/examples/sample_app/dist/app/views/layout/application.herb +5 -4
- data/examples/sample_app/dist/app/views/layout/foo.herb +1 -1
- data/examples/sample_app/dist/app/views/posts/new.herb +9 -2
- data/examples/sample_app/dist/app/views/shared/_test.herb +1 -0
- data/examples/sample_app/dist/conf/merb.yml +7 -7
- data/examples/sample_app/dist/conf/merb_init.rb +8 -1
- data/examples/sample_app/dist/conf/mup.conf +5 -11
- data/examples/sample_app/dist/conf/router.rb +1 -1
- data/examples/sample_app/dist/public/test.html +5 -0
- data/examples/sample_app/dist/schema/migrations/002_add_sessions_table.rb +1 -1
- data/examples/sample_app/dist/schema/schema.rb +1 -1
- data/examples/sample_app/log/merb.4000.pid +1 -0
- data/lib/merb.rb +35 -17
- data/lib/merb/core_ext.rb +2 -0
- data/lib/merb/{merb_class_extensions.rb → core_ext/merb_class.rb} +42 -0
- data/lib/merb/core_ext/merb_enumerable.rb +7 -0
- data/lib/merb/{merb_utils.rb → core_ext/merb_hash.rb} +1 -78
- data/lib/merb/core_ext/merb_kernel.rb +16 -0
- data/lib/merb/core_ext/merb_module.rb +10 -0
- data/lib/merb/core_ext/merb_numeric.rb +20 -0
- data/lib/merb/core_ext/merb_object.rb +6 -0
- data/lib/merb/core_ext/merb_string.rb +40 -0
- data/lib/merb/core_ext/merb_symbol.rb +12 -0
- data/lib/merb/merb_constants.rb +18 -0
- data/lib/merb/merb_controller.rb +150 -76
- data/lib/merb/{session/merb_drb_server.rb → merb_drb_server.rb} +13 -46
- data/lib/merb/merb_exceptions.rb +4 -0
- data/lib/merb/merb_handler.rb +29 -17
- data/lib/merb/merb_request.rb +95 -0
- data/lib/merb/merb_upload_handler.rb +46 -0
- data/lib/merb/merb_upload_progress.rb +48 -0
- data/lib/merb/merb_view_context.rb +46 -0
- data/lib/merb/merb_yaml_store.rb +31 -0
- data/lib/merb/mixins/basic_authentication_mixin.rb +2 -2
- data/lib/merb/mixins/controller_mixin.rb +24 -75
- data/lib/merb/mixins/erubis_capture_mixin.rb +84 -0
- data/lib/merb/mixins/javascript_mixin.rb +103 -19
- data/lib/merb/mixins/merb_status_codes.rb +59 -0
- data/lib/merb/mixins/render_mixin.rb +114 -40
- data/lib/merb/mixins/responder_mixin.rb +2 -1
- data/lib/merb/session/merb_ar_session.rb +120 -0
- data/lib/merb/session/merb_drb_session.rb +0 -6
- data/lib/merb/vendor/paginator/paginator.rb +102 -99
- metadata +44 -8
- data/examples/sample_app/script/startdrb +0 -8
- data/lib/merb/session/merb_session.rb +0 -64
- data/lib/mutex_hotfix.rb +0 -34
data/lib/merb/merb_handler.rb
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
|
2
1
|
class MerbHandler < Mongrel::HttpHandler
|
3
2
|
@@file_only_methods = ["GET","HEAD"]
|
4
3
|
|
@@ -31,7 +30,7 @@ class MerbHandler < Mongrel::HttpHandler
|
|
31
30
|
return
|
32
31
|
end
|
33
32
|
|
34
|
-
MERB_LOGGER.info("
|
33
|
+
MERB_LOGGER.info("\nRequest: PATH_INFO: #{request.params[Mongrel::Const::PATH_INFO]} (#{Time.now.strftime("%Y-%m-%d %H:%M:%S")})")
|
35
34
|
|
36
35
|
# Rails style page caching. Check the public dir first for
|
37
36
|
# .html pages and serve directly. Otherwise fall back to Merb
|
@@ -58,18 +57,16 @@ class MerbHandler < Mongrel::HttpHandler
|
|
58
57
|
controller, action = handle(request)
|
59
58
|
MERB_LOGGER.info("Routing to controller: #{controller.class} action: #{action}\nParsing HTTP Input took: #{Time.now - start} seconds")
|
60
59
|
|
61
|
-
#
|
62
|
-
#
|
63
|
-
#
|
64
|
-
#
|
65
|
-
|
66
|
-
|
60
|
+
# We need a mutex here because ActiveRecord code can be run
|
61
|
+
# in your controller actions. AR performs much better in single
|
62
|
+
# threaded mode so we lock here for the shortest amount of time
|
63
|
+
# possible. Route recognition and mime parsing has already occured
|
64
|
+
# at this point because those processes are thread safe. This
|
65
|
+
# gives us the best trade off for multi threaded performance
|
66
|
+
# of thread safe, and a lock around calls to your controller actions.
|
67
|
+
@guard.synchronize {
|
67
68
|
controller.dispatch(action)
|
68
|
-
|
69
|
-
@guard.synchronize {
|
70
|
-
controller.dispatch(action)
|
71
|
-
}
|
72
|
-
end
|
69
|
+
}
|
73
70
|
rescue Exception => e
|
74
71
|
response.start(500) do |head,out|
|
75
72
|
head["Content-Type"] = "text/html"
|
@@ -98,10 +95,25 @@ class MerbHandler < Mongrel::HttpHandler
|
|
98
95
|
|
99
96
|
if sendfile
|
100
97
|
MERB_LOGGER.info("X-SENDFILE: #{sendfile}\nComplete Request took: #{Time.now - start} seconds")
|
101
|
-
|
102
|
-
response.
|
98
|
+
file_status = File.stat(sendfile)
|
99
|
+
response.status = 200
|
100
|
+
# Set the last modified times as well and etag for all files
|
101
|
+
response.header[Mongrel::Const::LAST_MODIFIED] = file_status.mtime.httpdate
|
102
|
+
# Calculated the same as apache, not sure how well the works on win32
|
103
|
+
response.header[Mongrel::Const::ETAG] = Mongrel::Const::ETAG_FORMAT % [file_status.mtime.to_i, file_status.size, file_status.ino]
|
104
|
+
# send a status with out content length
|
105
|
+
response.send_status(file_status.size)
|
103
106
|
response.send_header
|
104
107
|
response.send_file(sendfile)
|
108
|
+
elsif controller.body.respond_to? :read
|
109
|
+
response.send_status(clength)
|
110
|
+
response.send_header
|
111
|
+
while chunk = controller.body.read(16384)
|
112
|
+
response.write(chunk)
|
113
|
+
end
|
114
|
+
if controller.body.respond_to? :close
|
115
|
+
controller.body.close
|
116
|
+
end
|
105
117
|
else
|
106
118
|
MERB_LOGGER.info("Response status: #{response.status}\nComplete Request took: #{Time.now - start} seconds\n\n")
|
107
119
|
# render response from successful controller
|
@@ -131,12 +143,12 @@ class MerbHandler < Mongrel::HttpHandler
|
|
131
143
|
# this is where your Merb::Controller is instantiated.
|
132
144
|
def instantiate_controller(controller_name, req, env, params)
|
133
145
|
if !File.exist?(Merb::Server.config[:dist_root]+"/app/controllers/#{controller_name.snake_case}.rb")
|
134
|
-
|
146
|
+
raise Merb::MissingControllerFile
|
135
147
|
end
|
136
148
|
begin
|
137
149
|
controller_name.import
|
138
150
|
return Object.const_get( controller_name.camel_case ).new(req, env, params)
|
139
|
-
rescue
|
151
|
+
rescue RuntimeError
|
140
152
|
warn "Error getting instance of '#{controller_name.camel_case}': #{$!}"
|
141
153
|
raise $!
|
142
154
|
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
module Merb
|
2
|
+
|
3
|
+
class Request
|
4
|
+
|
5
|
+
def initialize(env, method)
|
6
|
+
@env = env
|
7
|
+
@method = method
|
8
|
+
end
|
9
|
+
|
10
|
+
# returns true if the request is an ajax request.
|
11
|
+
def xml_http_request?
|
12
|
+
not /XMLHttpRequest/i.match(@env['HTTP_X_REQUESTED_WITH']).nil?
|
13
|
+
end
|
14
|
+
alias xhr? :xml_http_request?
|
15
|
+
alias ajax? :xml_http_request?
|
16
|
+
|
17
|
+
# returns the remote IP address if it can find it.
|
18
|
+
def remote_ip
|
19
|
+
return @env['HTTP_CLIENT_IP'] if @env.include?('HTTP_CLIENT_IP')
|
20
|
+
|
21
|
+
if @env.include?(Mongrel::Const::HTTP_X_FORWARDED_FOR) then
|
22
|
+
remote_ips = @env[Mongrel::Const::HTTP_X_FORWARDED_FOR].split(',').reject do |ip|
|
23
|
+
ip =~ /^unknown$|^(127|10|172\.16|192\.168)\./i
|
24
|
+
end
|
25
|
+
|
26
|
+
return remote_ips.first.strip unless remote_ips.empty?
|
27
|
+
end
|
28
|
+
|
29
|
+
return @env[Mongrel::Const::REMOTE_ADDR]
|
30
|
+
end
|
31
|
+
|
32
|
+
# returns either 'https://' or 'http://' depending on
|
33
|
+
# the HTTPS header
|
34
|
+
def protocol
|
35
|
+
ssl? ? 'https://' : 'http://'
|
36
|
+
end
|
37
|
+
|
38
|
+
# returns true if the request is an SSL request
|
39
|
+
def ssl?
|
40
|
+
@env['HTTPS'] == 'on' || @env['HTTP_X_FORWARDED_PROTO'] == 'https'
|
41
|
+
end
|
42
|
+
|
43
|
+
# returns he request uri.
|
44
|
+
def uri
|
45
|
+
@env['REQUEST_URI']
|
46
|
+
end
|
47
|
+
|
48
|
+
# returns the uri without the query string.
|
49
|
+
def path
|
50
|
+
uri ? uri.split('?').first : ''
|
51
|
+
end
|
52
|
+
|
53
|
+
# returns the PATH_INFO
|
54
|
+
def path_info
|
55
|
+
@env['PATH_INFO']
|
56
|
+
end
|
57
|
+
|
58
|
+
# returns the port the server is running on
|
59
|
+
def port
|
60
|
+
@env['SERVER_PORT'].to_i
|
61
|
+
end
|
62
|
+
|
63
|
+
# returns the full hostname including port
|
64
|
+
def host
|
65
|
+
@env['HTTP_X_FORWARDED_HOST'] || @env['HTTP_HOST']
|
66
|
+
end
|
67
|
+
|
68
|
+
# returns an array of all the subdomain parts of the host.
|
69
|
+
def subdomains(tld_length = 1)
|
70
|
+
parts = host.split('.')
|
71
|
+
parts[0..-(tld_length+2)]
|
72
|
+
end
|
73
|
+
|
74
|
+
# returns the full domain name without the port number.
|
75
|
+
def domain(tld_length = 1)
|
76
|
+
host.split('.').last(1 + tld_length).join('.').sub(/:\d+$/,'')
|
77
|
+
end
|
78
|
+
|
79
|
+
# returns the REQUEST_METHOD
|
80
|
+
def method
|
81
|
+
@method ||= @env['REQUEST_METHOD']
|
82
|
+
end
|
83
|
+
|
84
|
+
# create predicate methods for querying the REQUEST_METHOD
|
85
|
+
[:get, :post, :put, :delete, :head].each do |m|
|
86
|
+
eval %{
|
87
|
+
def #{m}?; method == :#{m}; end
|
88
|
+
}
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
94
|
+
|
95
|
+
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'mongrel'
|
2
|
+
|
3
|
+
|
4
|
+
class MerbUploadHandler < Mongrel::HttpHandler
|
5
|
+
|
6
|
+
def initialize(options = {})
|
7
|
+
@path_info = Array(options[:path_info])
|
8
|
+
@frequency = options[:frequency] || 3
|
9
|
+
@request_notify = true
|
10
|
+
if options[:drb]
|
11
|
+
require 'drb'
|
12
|
+
DRb.start_service
|
13
|
+
Mongrel.const_set :Uploads, DRbObject.new(nil, "druby://#{Merb::Server.config[:host]}:#{Merb::Server.config[:session]}").upload_progress
|
14
|
+
else
|
15
|
+
require File.dirname(__FILE__)+'/merb_upload_progress'
|
16
|
+
Mongrel.const_set :Uploads, Merb::UploadProgress.new
|
17
|
+
end
|
18
|
+
Mongrel::Uploads.debug = true if options[:debug]
|
19
|
+
end
|
20
|
+
|
21
|
+
def request_begins(params)
|
22
|
+
upload_notify(:add, params, params[Mongrel::Const::CONTENT_LENGTH].to_i)
|
23
|
+
end
|
24
|
+
|
25
|
+
def request_progress(params, clen, total)
|
26
|
+
upload_notify(:mark, params, clen)
|
27
|
+
end
|
28
|
+
|
29
|
+
def process(request, response)
|
30
|
+
upload_notify(:finish, request.params)
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
def upload_notify(action, params, *args)
|
35
|
+
return unless @path_info.include?(params['PATH_INFO']) &&
|
36
|
+
params[Mongrel::Const::REQUEST_METHOD] == 'POST' &&
|
37
|
+
upload_id = Mongrel::HttpRequest.query_parse(params['QUERY_STRING'])['upload_id']
|
38
|
+
if action == :mark
|
39
|
+
last_checked_time = Mongrel::Uploads.last_checked(upload_id)
|
40
|
+
return unless last_checked_time && Time.now - last_checked_time > @frequency
|
41
|
+
end
|
42
|
+
Mongrel::Uploads.send(action, upload_id, *args)
|
43
|
+
Mongrel::Uploads.update_checked_time(upload_id) unless action == :finish
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Merb
|
2
|
+
# Keeps track of the status of all currently processing uploads
|
3
|
+
class UploadProgress
|
4
|
+
include DRbUndumped
|
5
|
+
attr_accessor :debug
|
6
|
+
def initialize
|
7
|
+
@guard = Mutex.new
|
8
|
+
@counters = {}
|
9
|
+
end
|
10
|
+
|
11
|
+
def check(upid)
|
12
|
+
@counters[upid].last rescue nil
|
13
|
+
end
|
14
|
+
|
15
|
+
def last_checked(upid)
|
16
|
+
@counters[upid].first rescue nil
|
17
|
+
end
|
18
|
+
|
19
|
+
def update_checked_time(upid)
|
20
|
+
@guard.synchronize { @counters[upid][0] = Time.now }
|
21
|
+
end
|
22
|
+
|
23
|
+
def add(upid, size)
|
24
|
+
@guard.synchronize do
|
25
|
+
@counters[upid] = [Time.now, {:size => size, :received => 0}]
|
26
|
+
puts "#{upid}: Added" if @debug
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def mark(upid, len)
|
31
|
+
return unless status = check(upid)
|
32
|
+
puts "#{upid}: Marking" if @debug
|
33
|
+
@guard.synchronize { status[:received] = status[:size] - len }
|
34
|
+
end
|
35
|
+
|
36
|
+
def finish(upid)
|
37
|
+
@guard.synchronize do
|
38
|
+
puts "#{upid}: Finished" if @debug
|
39
|
+
@counters.delete(upid)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def list
|
44
|
+
@counters.keys.sort
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require File.dirname(__FILE__)+'/mixins/erubis_capture_mixin'
|
2
|
+
require File.dirname(__FILE__)+'/mixins/javascript_mixin'
|
3
|
+
|
4
|
+
module Merb
|
5
|
+
|
6
|
+
# the ViewContext is really
|
7
|
+
# just an empty container for us to fill with instance
|
8
|
+
# variables from the controller, include helpers into
|
9
|
+
# and then use as the context object passed to Erubis
|
10
|
+
# when evaluating the templates.
|
11
|
+
class ViewContext
|
12
|
+
include Merb::ErubisCaptureMixin
|
13
|
+
include Merb::JavascriptMixin
|
14
|
+
|
15
|
+
def initialize(controller)
|
16
|
+
@controller = controller
|
17
|
+
(@controller.instance_variables - %w[@cookies @session @headers @params
|
18
|
+
@env @in @status @root @method @request @fingerprint_before
|
19
|
+
@controller @k]).each do |ivar|
|
20
|
+
self.instance_variable_set(ivar, @controller.instance_variable_get(ivar))
|
21
|
+
end
|
22
|
+
begin
|
23
|
+
self.class.class_eval("include Merb::#{@controller.class.name}Helper")
|
24
|
+
rescue NameError
|
25
|
+
MERB_LOGGER.info("Missing Helper: Merb::#{@controller.class.name}Helper")
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# accessor for the view. refers to the current @controller object
|
30
|
+
def controller
|
31
|
+
@controller
|
32
|
+
end
|
33
|
+
|
34
|
+
# catch any method calls that the controller responds to
|
35
|
+
# and delegate them back to the controller.
|
36
|
+
def method_missing(sym, *args, &blk)
|
37
|
+
if @controller.respond_to? sym
|
38
|
+
@controller.send(sym, *args, &blk)
|
39
|
+
else
|
40
|
+
super
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'yaml/store'
|
2
|
+
|
3
|
+
module Merb
|
4
|
+
# thanks to Michael Fellinger from Ramaze
|
5
|
+
class SimpleModel
|
6
|
+
attr_accessor :db
|
7
|
+
|
8
|
+
def initialize(filename = "#{DIST_ROOT}/schema/db.yaml")
|
9
|
+
FileUtils.touch(filename)
|
10
|
+
@db = YAML::Store.new(filename)
|
11
|
+
end
|
12
|
+
|
13
|
+
def method_missing(meth, *args, &block)
|
14
|
+
@db.transaction do
|
15
|
+
@db.send(meth, *args, &block)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def [](key)
|
20
|
+
@db.transaction do
|
21
|
+
@db[key]
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def []=(key, value)
|
26
|
+
@db.transaction do
|
27
|
+
@db[key] = value
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -18,13 +18,13 @@ module Merb
|
|
18
18
|
|
19
19
|
def authenticate
|
20
20
|
if !authenticated?
|
21
|
-
throw :halt
|
21
|
+
throw :halt, :access_denied
|
22
22
|
end
|
23
23
|
end
|
24
24
|
|
25
25
|
def self.included(base)
|
26
26
|
base.class_eval do
|
27
|
-
def
|
27
|
+
def access_denied
|
28
28
|
@status = 401
|
29
29
|
@headers['Content-type'] = 'text/plain'
|
30
30
|
@headers['Status'] = 'Unauthorized'
|
@@ -14,8 +14,22 @@ module Merb
|
|
14
14
|
# pass in a path to a file and this will set the
|
15
15
|
# right headers and let mongrel do its thang and
|
16
16
|
# serve the static file directly.
|
17
|
-
def send_file(file)
|
18
|
-
|
17
|
+
def send_file(file, opts={})
|
18
|
+
opts.update(Merb::Const::DEFAULT_SEND_FILE_OPTIONS.merge(opts))
|
19
|
+
[:type, :disposition].each do |arg|
|
20
|
+
raise ArgumentError, ":#{arg} option required" if opts[arg].nil?
|
21
|
+
end
|
22
|
+
|
23
|
+
disposition = opts[:disposition].dup || 'attachment'
|
24
|
+
|
25
|
+
disposition << %(; filename="#{opts[:filename] ? opts[:filename] : File.basename(file)}")
|
26
|
+
|
27
|
+
headers.update(
|
28
|
+
'Content-Type' => opts[:type].strip, # fixes a problem with extra '\r' with some browsers
|
29
|
+
'Content-Disposition' => disposition,
|
30
|
+
'Content-Transfer-Encoding' => 'binary',
|
31
|
+
'X-SENDFILE' => file
|
32
|
+
)
|
19
33
|
return
|
20
34
|
end
|
21
35
|
|
@@ -41,10 +55,18 @@ module Merb
|
|
41
55
|
}
|
42
56
|
end
|
43
57
|
|
58
|
+
# creates a random token like:
|
59
|
+
# "b9a82e011694cc13a4249731b9e83cea"
|
44
60
|
def make_token
|
45
61
|
require 'digest/md5'
|
46
62
|
Digest::MD5.hexdigest("#{inspect}#{Time.now}#{rand}")
|
47
63
|
end
|
64
|
+
|
65
|
+
def escape_xml(obj)
|
66
|
+
obj.to_s.gsub(/[&<>"']/) { |s| Merb::Const::ESCAPE_TABLE[s] }
|
67
|
+
end
|
68
|
+
alias h escape_xml
|
69
|
+
alias html_escape escape_xml
|
48
70
|
|
49
71
|
# does url escaping
|
50
72
|
def escape(s)
|
@@ -56,78 +78,5 @@ module Merb
|
|
56
78
|
Mongrel::HttpRequest.unescape(s)
|
57
79
|
end
|
58
80
|
|
59
|
-
# returns true if the request is an ajax request.
|
60
|
-
def xml_http_request?
|
61
|
-
not /XMLHttpRequest/i.match(@env['HTTP_X_REQUESTED_WITH']).nil?
|
62
|
-
end
|
63
|
-
alias xhr? :xml_http_request?
|
64
|
-
alias ajax? :xml_http_request?
|
65
|
-
|
66
|
-
# returns the remote IP address if it can find it.
|
67
|
-
def remote_ip
|
68
|
-
return @env['HTTP_CLIENT_IP'] if @env.include?('HTTP_CLIENT_IP')
|
69
|
-
|
70
|
-
if @env.include?(Mongrel::Const::HTTP_X_FORWARDED_FOR) then
|
71
|
-
remote_ips = @env[Mongrel::Const::HTTP_X_FORWARDED_FOR].split(',').reject do |ip|
|
72
|
-
ip =~ /^unknown$|^(127|10|172\.16|192\.168)\./i
|
73
|
-
end
|
74
|
-
|
75
|
-
return remote_ips.first.strip unless remote_ips.empty?
|
76
|
-
end
|
77
|
-
|
78
|
-
return @env[Mongrel::Const::REMOTE_ADDR]
|
79
|
-
end
|
80
|
-
|
81
|
-
# returns either 'https://' or 'http://' depending on
|
82
|
-
# the HTTPS header
|
83
|
-
def protocol
|
84
|
-
@env['HTTPS'] == 'on' ? 'https://' : 'http://'
|
85
|
-
end
|
86
|
-
|
87
|
-
# returns true if the request is an SSL request
|
88
|
-
def ssl?
|
89
|
-
@env['HTTPS'] == 'on'
|
90
|
-
end
|
91
|
-
|
92
|
-
# The request uri.
|
93
|
-
def uri
|
94
|
-
@env['REQUEST_URI']
|
95
|
-
end
|
96
|
-
|
97
|
-
# The path is the uri without the query string.
|
98
|
-
def path
|
99
|
-
uri ? uri.split('?').first : ''
|
100
|
-
end
|
101
|
-
|
102
|
-
def path_info
|
103
|
-
@env['PATH_INFO']
|
104
|
-
end
|
105
|
-
|
106
|
-
def port
|
107
|
-
@env['SERVER_PORT'].to_i
|
108
|
-
end
|
109
|
-
|
110
|
-
def host
|
111
|
-
@env['HTTP_X_FORWARDED_HOST'] || @env['HTTP_HOST']
|
112
|
-
end
|
113
|
-
|
114
|
-
def subdomains(tld_length = 1)
|
115
|
-
parts = host.split('.')
|
116
|
-
parts[0..-(tld_length+2)]
|
117
|
-
end
|
118
|
-
|
119
|
-
def domain(tld_length = 1)
|
120
|
-
host.split('.').last(1 + tld_length).join('.')
|
121
|
-
end
|
122
|
-
|
123
|
-
def method
|
124
|
-
@method ||= @env['REQUEST_METHOD']
|
125
|
-
end
|
126
|
-
|
127
|
-
[:get, :post, :put, :delete, :head].each do |m|
|
128
|
-
eval %{
|
129
|
-
def #{m}?; method == :#{m}; end
|
130
|
-
}
|
131
|
-
end
|
132
81
|
end
|
133
82
|
end
|