merb 0.0.7 → 0.0.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.
- 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
|