merb-core 1.1.0.rc1 → 1.1.0
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/lib/merb-core/bootloader.rb +4 -5
- data/lib/merb-core/config.rb +3 -0
- data/lib/merb-core/controller/mixins/conditional_get.rb +64 -49
- data/lib/merb-core/controller/mixins/controller.rb +9 -1
- data/lib/merb-core/controller/template.rb +4 -0
- data/lib/merb-core/dispatch/dispatcher.rb +0 -2
- data/lib/merb-core/dispatch/request_parsers.rb +6 -1
- data/lib/merb-core/dispatch/session/store_container.rb +14 -1
- data/lib/merb-core/rack/handler/mongrel.rb +6 -2
- data/lib/merb-core/test/helpers/multipart_request_helper.rb +0 -1
- data/lib/merb-core/version.rb +1 -1
- data/spec/public/controller/conditional_get_spec.rb +76 -11
- data/spec/public/controller/controllers/conditional_get.rb +4 -0
- data/spec/public/controller/controllers/redirect.rb +13 -1
- data/spec/public/controller/redirect_spec.rb +12 -0
- data/spec/public/core/config_spec.rb +18 -0
- data/spec/public/request/multipart_spec.rb +37 -18
- data/spec/public/session/memory_session_spec.rb +31 -1
- metadata +7 -9
data/lib/merb-core/bootloader.rb
CHANGED
@@ -372,15 +372,12 @@ class Merb::BootLoader::Dependencies < Merb::BootLoader
|
|
372
372
|
# :api: plugin
|
373
373
|
def self.run
|
374
374
|
set_encoding
|
375
|
-
|
376
|
-
# then environment init file, then start enabling specific
|
377
|
-
# components, load dependencies and update logger.
|
375
|
+
load_dependencies
|
378
376
|
unless Merb::disabled?(:initfile)
|
379
377
|
load_initfile
|
380
378
|
load_env_config
|
381
379
|
end
|
382
380
|
expand_ruby_path
|
383
|
-
load_dependencies
|
384
381
|
update_logger
|
385
382
|
nil
|
386
383
|
end
|
@@ -1036,7 +1033,6 @@ class Merb::BootLoader::LoadClasses < Merb::BootLoader
|
|
1036
1033
|
error_map = {}
|
1037
1034
|
|
1038
1035
|
klasses.each do |klass|
|
1039
|
-
klasses.delete(klass)
|
1040
1036
|
begin
|
1041
1037
|
load_file klass
|
1042
1038
|
rescue NameError => ne
|
@@ -1044,6 +1040,7 @@ class Merb::BootLoader::LoadClasses < Merb::BootLoader
|
|
1044
1040
|
failed_classes.push(klass)
|
1045
1041
|
end
|
1046
1042
|
end
|
1043
|
+
klasses.clear
|
1047
1044
|
|
1048
1045
|
# Keep list of classes unique
|
1049
1046
|
failed_classes.each { |k| klasses.push(k) unless klasses.include?(k) }
|
@@ -1317,6 +1314,8 @@ class Merb::BootLoader::RackUpApplication < Merb::BootLoader
|
|
1317
1314
|
Merb::Config[:app] = eval("::Rack::Builder.new {( #{rackup_code}\n )}.to_app", TOPLEVEL_BINDING, Merb::Config[:rackup])
|
1318
1315
|
else
|
1319
1316
|
Merb::Config[:app] = ::Rack::Builder.new {
|
1317
|
+
use Rack::Head # handle head requests
|
1318
|
+
use Merb::Rack::ContentLength # report content length
|
1320
1319
|
if prefix = ::Merb::Config[:path_prefix]
|
1321
1320
|
use Merb::Rack::PathPrefix, prefix
|
1322
1321
|
end
|
data/lib/merb-core/config.rb
CHANGED
@@ -7,94 +7,109 @@
|
|
7
7
|
# +request_fresh?+ that is used after setting of
|
8
8
|
# last modification time or ETag:
|
9
9
|
#
|
10
|
-
#
|
10
|
+
#
|
11
|
+
# @example
|
12
|
+
# def show
|
13
|
+
# self.etag = Digest::SHA1.hexdigest(calculate_cache_key(params))
|
11
14
|
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
#
|
15
|
-
#
|
16
|
-
#
|
17
|
-
#
|
18
|
-
#
|
19
|
-
# @product = Product.get(params[:id])
|
20
|
-
# display @product
|
15
|
+
# if request_fresh?
|
16
|
+
# self.status = 304
|
17
|
+
# return ''
|
18
|
+
# else
|
19
|
+
# @product = Product.get(params[:id])
|
20
|
+
# display @product
|
21
|
+
# end
|
21
22
|
# end
|
22
|
-
# end
|
23
23
|
module Merb::ConditionalGetMixin
|
24
24
|
|
25
|
-
# Sets ETag response header by calling
|
26
|
-
#
|
25
|
+
# Sets ETag response header by calling #to_s on the argument
|
26
|
+
#
|
27
|
+
# @param tag [#to_s] value of ETag header
|
27
28
|
#
|
28
|
-
#
|
29
|
-
# tag<~to_s>::
|
30
|
-
# value of ETag header enclosed in double quotes
|
31
|
-
# as required by the RFC
|
29
|
+
# @return [String] value of ETag header enclosed in double quotes as required by the RFC
|
32
30
|
#
|
33
|
-
#
|
31
|
+
# @api public
|
34
32
|
def etag=(tag)
|
35
33
|
headers[Merb::Const::ETAG] = %("#{tag}")
|
36
34
|
end
|
37
35
|
|
38
|
-
#
|
39
|
-
#
|
40
|
-
#
|
36
|
+
# Value of the ETag header
|
37
|
+
#
|
38
|
+
# @return [String] Value of ETag response header if set.
|
39
|
+
# @return [nil] If ETag header not set.
|
41
40
|
#
|
42
|
-
#
|
41
|
+
# @api public
|
43
42
|
def etag
|
44
43
|
headers[Merb::Const::ETAG]
|
45
44
|
end
|
46
45
|
|
47
|
-
#
|
48
|
-
# <Boolean>::
|
49
|
-
# true if ETag response header equals If-None-Match request header,
|
50
|
-
# false otherwise
|
46
|
+
# Test to see if the request's Etag matches the one supplied locally
|
51
47
|
#
|
52
|
-
#
|
48
|
+
# @return [true] if ETag response header equals If-None-Match request header
|
49
|
+
# @return [true] if it does not.
|
50
|
+
#
|
51
|
+
# @api public
|
53
52
|
def etag_matches?(tag = self.etag)
|
54
53
|
tag == self.request.if_none_match
|
55
54
|
end
|
56
55
|
|
57
|
-
# Sets Last-Modified response header
|
56
|
+
# Sets Last-Modified response header
|
57
|
+
#
|
58
|
+
# @param time [Time,DateTime] The last modified time of the resource
|
58
59
|
#
|
59
|
-
#
|
60
|
-
# tag<Time>::
|
61
|
-
# resource modification timestamp converted into format
|
62
|
-
# required by the RFC
|
60
|
+
# @return [String] The last modified time of the resource in the format required by the RFC
|
63
61
|
#
|
64
|
-
#
|
62
|
+
# @api public
|
65
63
|
def last_modified=(time)
|
66
64
|
time = time.to_time if time.is_a?(DateTime)
|
67
65
|
# time.utc.strftime("%a, %d %b %Y %X") if we could rely on locale being American
|
68
66
|
headers[Merb::Const::LAST_MODIFIED] = time.httpdate
|
69
67
|
end
|
70
68
|
|
71
|
-
#
|
72
|
-
#
|
73
|
-
#
|
69
|
+
# Value of the Last-Modified header
|
70
|
+
#
|
71
|
+
# @return [Time] Value of Last-Modified response header if set.
|
72
|
+
# @return [nil] If Last-Modified not set.
|
74
73
|
#
|
75
|
-
#
|
74
|
+
# @api public
|
76
75
|
def last_modified
|
77
76
|
last_mod = headers[Merb::Const::LAST_MODIFIED]
|
78
77
|
Time.rfc2822(last_mod) if last_mod
|
79
78
|
end
|
80
79
|
|
81
|
-
#
|
82
|
-
# <Boolean>::
|
83
|
-
# true if Last-Modified response header is < than
|
84
|
-
# If-Modified-Since request header value, false otherwise.
|
80
|
+
# Test to see if the request's If-Modified-Since is satisfied
|
85
81
|
#
|
86
|
-
#
|
82
|
+
# @param time [Time] Time to test if the If-Modified-Since header against
|
83
|
+
#
|
84
|
+
# @return [true] Last-Modified response header is < than If-Modified-Since request header
|
85
|
+
# @return [false] otherwise
|
86
|
+
#
|
87
|
+
# @api public
|
87
88
|
def not_modified?(time = self.last_modified)
|
88
|
-
request.if_modified_since
|
89
|
+
if !request.if_modified_since.nil? and !time.nil?
|
90
|
+
time <= request.if_modified_since
|
91
|
+
else
|
92
|
+
false
|
93
|
+
end
|
89
94
|
end
|
90
95
|
|
91
|
-
#
|
92
|
-
#
|
93
|
-
#
|
94
|
-
# so request is fresh; false otherwise
|
96
|
+
# Tests freshness of response using all supplied validators
|
97
|
+
#
|
98
|
+
# A response with no validators is always stale.
|
95
99
|
#
|
96
|
-
#
|
100
|
+
# @return [true] ETag matches and entity is not modified
|
101
|
+
# @return [false] One or more validators failed, or none were supplied
|
102
|
+
#
|
103
|
+
# @api public
|
97
104
|
def request_fresh?
|
98
|
-
|
105
|
+
# make sure we have something to compare too.
|
106
|
+
return false unless last_modified or etag
|
107
|
+
|
108
|
+
fresh = true
|
109
|
+
|
110
|
+
# only check if we have set the right headers
|
111
|
+
fresh &&= etag_matches?(self.etag) if etag
|
112
|
+
fresh &&= not_modified?(self.last_modified) if last_modified
|
113
|
+
fresh
|
99
114
|
end
|
100
115
|
end
|
@@ -124,6 +124,10 @@ module Merb
|
|
124
124
|
# Shorthand for common usage :message => {:error => "..."}
|
125
125
|
# :success<String>::
|
126
126
|
# Shorthand for common usage :message => {:success => "..."}
|
127
|
+
# :status<String, Symbol>::
|
128
|
+
# Status code to set for the response. Can be any valid redirect
|
129
|
+
# status. Has precedence over the :permanent parameter, which is
|
130
|
+
# retained for convenience.
|
127
131
|
#
|
128
132
|
# ==== Returns
|
129
133
|
# String:: Explanation of redirect.
|
@@ -141,7 +145,11 @@ module Merb
|
|
141
145
|
opts = default_redirect_options.merge(opts)
|
142
146
|
|
143
147
|
url = handle_redirect_messages(url,opts)
|
144
|
-
|
148
|
+
|
149
|
+
_status = opts[:status] if opts[:status]
|
150
|
+
_status ||= opts[:permanent] ? 301 : 302
|
151
|
+
self.status = _status
|
152
|
+
|
145
153
|
Merb.logger.info("Redirecting to: #{url} (#{self.status})")
|
146
154
|
headers['Location'] = url
|
147
155
|
"<html><body>You are being <a href=\"#{url}\">redirected</a>.</body></html>"
|
@@ -187,6 +187,10 @@ module Merb::Template
|
|
187
187
|
require 'erubis'
|
188
188
|
|
189
189
|
class Erubis
|
190
|
+
# Fixing bug in Erubis
|
191
|
+
# http://rubyforge.org/tracker/index.php?func=detail&aid=21825&group_id=1320&atid=5201
|
192
|
+
XmlHelper = ::Erubis::XmlHelper
|
193
|
+
|
190
194
|
# ==== Parameters
|
191
195
|
# io<#path>:: An IO containing the full path of the template.
|
192
196
|
# name<String>:: The name of the method that will be created.
|
@@ -127,12 +127,17 @@ module Merb
|
|
127
127
|
else
|
128
128
|
data = body
|
129
129
|
end
|
130
|
+
|
130
131
|
unless key_memo.include?(name) && name !~ /\[\]/
|
131
132
|
paramhsh = normalize_params(paramhsh,name,data)
|
132
133
|
end
|
133
|
-
|
134
|
+
|
135
|
+
# Prevent from double processing files but process other params
|
136
|
+
key_memo << name if filename && !filename.empty?
|
137
|
+
|
134
138
|
break if buf.empty? || content_length == -1
|
135
139
|
}
|
140
|
+
|
136
141
|
paramhsh
|
137
142
|
end
|
138
143
|
|
@@ -6,6 +6,9 @@ module Merb
|
|
6
6
|
# :api: private
|
7
7
|
attr_accessor :_fingerprint
|
8
8
|
|
9
|
+
# Determines how many times to try generating a unique session key before we give up
|
10
|
+
GENERATE_MAX_TRIES = 100
|
11
|
+
|
9
12
|
# The class attribute :store holds a reference to an object that implements
|
10
13
|
# the following interface:
|
11
14
|
#
|
@@ -54,7 +57,17 @@ module Merb
|
|
54
57
|
#
|
55
58
|
# :api: private
|
56
59
|
def generate
|
57
|
-
|
60
|
+
|
61
|
+
# make sure we generate a unique session uuid
|
62
|
+
sid = nil
|
63
|
+
GENERATE_MAX_TRIES.times do |i|
|
64
|
+
sid = Merb::SessionMixin.rand_uuid
|
65
|
+
data = store.retrieve_session(sid) rescue nil
|
66
|
+
break if data.nil?
|
67
|
+
raise "Unable to Generate Unique Session key" if i == (GENERATE_MAX_TRIES-1)
|
68
|
+
end
|
69
|
+
|
70
|
+
session = new(sid)
|
58
71
|
session.needs_new_cookie = true
|
59
72
|
session
|
60
73
|
end
|
@@ -83,16 +83,20 @@ module Merb
|
|
83
83
|
|
84
84
|
begin
|
85
85
|
response.status = status.to_i
|
86
|
+
response.send_status(body.respond_to?(:length) ? body.length : nil)
|
87
|
+
|
86
88
|
headers.each { |k, vs|
|
87
89
|
Array(vs).each { |v|
|
88
90
|
response.header[k] = v
|
89
91
|
}
|
90
92
|
}
|
93
|
+
response.send_header
|
91
94
|
|
92
95
|
body.each { |part|
|
93
|
-
response.
|
96
|
+
response.write(part)
|
97
|
+
response.socket.flush
|
94
98
|
}
|
95
|
-
response.
|
99
|
+
response.done = true
|
96
100
|
ensure
|
97
101
|
body.close if body.respond_to? :close
|
98
102
|
end
|
data/lib/merb-core/version.rb
CHANGED
@@ -3,9 +3,9 @@ require File.join(File.dirname(__FILE__), "spec_helper")
|
|
3
3
|
Controllers = Merb::Test::Fixtures::Controllers
|
4
4
|
|
5
5
|
describe Merb::Controller, "#etag=" do
|
6
|
-
|
6
|
+
|
7
7
|
before do
|
8
|
-
Merb.push_path(:layout, File.dirname(__FILE__) / "controllers" / "views" / "layouts")
|
8
|
+
Merb.push_path(:layout, File.dirname(__FILE__) / "controllers" / "views" / "layouts")
|
9
9
|
Merb::Router.prepare do
|
10
10
|
default_routes
|
11
11
|
end
|
@@ -21,7 +21,7 @@ end
|
|
21
21
|
|
22
22
|
describe Merb::Controller, "#last_modified=" do
|
23
23
|
before do
|
24
|
-
Merb.push_path(:layout, File.dirname(__FILE__) / "controllers" / "views" / "layouts")
|
24
|
+
Merb.push_path(:layout, File.dirname(__FILE__) / "controllers" / "views" / "layouts")
|
25
25
|
Merb::Router.prepare do
|
26
26
|
default_routes
|
27
27
|
end
|
@@ -53,7 +53,7 @@ describe Merb::Controller, "#modified_since?" do
|
|
53
53
|
@controller = dispatch_to(Merb::Test::Fixtures::Controllers::ConditionalGet,
|
54
54
|
:sets_last_modified, {}, { Merb::Const::HTTP_IF_MODIFIED_SINCE => Time.at(7000).httpdate })
|
55
55
|
end
|
56
|
-
|
56
|
+
|
57
57
|
it 'return true when response Last-Modified header value <= request If-Modified-Since header' do
|
58
58
|
@controller.not_modified?(Time.at(5000)).should be(true)
|
59
59
|
@controller.not_modified?(Time.at(6999)).should be(true)
|
@@ -67,35 +67,100 @@ end
|
|
67
67
|
|
68
68
|
|
69
69
|
describe Merb::Controller, "#request_fresh?" do
|
70
|
+
it "returns false if no headers are supplied" do
|
71
|
+
@controller = dispatch_to(Merb::Test::Fixtures::Controllers::ConditionalGet,
|
72
|
+
:superfresh, {}, {})
|
73
|
+
@controller.request_fresh?.should be(false)
|
74
|
+
end
|
75
|
+
|
76
|
+
it "returns false if no validation information is supplied by the action and no headers are sent" do
|
77
|
+
@controller = dispatch_to(Merb::Test::Fixtures::Controllers::ConditionalGet,
|
78
|
+
:sets_nothing, {}, {})
|
79
|
+
@controller.request_fresh?.should be(false)
|
80
|
+
end
|
81
|
+
|
82
|
+
it "returns false if no validation information is supplied by the action and an ETag header is sent" do
|
83
|
+
env = { Merb::Const::HTTP_IF_NONE_MATCH => '"39791e6fb09"' }
|
84
|
+
@controller = dispatch_to(Merb::Test::Fixtures::Controllers::ConditionalGet,
|
85
|
+
:sets_nothing, {}, env)
|
86
|
+
@controller.request_fresh?.should be(false)
|
87
|
+
end
|
88
|
+
|
89
|
+
it "returns false if no validation information is supplied by the action and an If-Modified-Since header is sent" do
|
90
|
+
env = { Merb::Const::HTTP_IF_MODIFIED_SINCE => Time.at(7000).httpdate }
|
91
|
+
@controller = dispatch_to(Merb::Test::Fixtures::Controllers::ConditionalGet,
|
92
|
+
:sets_nothing, {}, env)
|
93
|
+
@controller.request_fresh?.should be(false)
|
94
|
+
end
|
95
|
+
|
96
|
+
it "returns false if no validation information is supplied by the action and both headers are sent" do
|
97
|
+
env = { 'HTTP_IF_MODIFIED_SINCE' => Time.at(7000).httpdate, Merb::Const::HTTP_IF_NONE_MATCH => '"39791e6fb09"' }
|
98
|
+
@controller = dispatch_to(Merb::Test::Fixtures::Controllers::ConditionalGet,
|
99
|
+
:sets_nothing, {}, env)
|
100
|
+
|
101
|
+
@controller.request_fresh?.should be(false)
|
102
|
+
end
|
103
|
+
|
104
|
+
|
70
105
|
it 'return true when ETag matches' do
|
71
|
-
env = {
|
106
|
+
env = { Merb::Const::HTTP_IF_NONE_MATCH => '"39791e6fb09"' }
|
72
107
|
@controller = dispatch_to(Merb::Test::Fixtures::Controllers::ConditionalGet,
|
73
108
|
:sets_etag, {}, env)
|
74
109
|
|
75
110
|
@controller.request_fresh?.should be(true)
|
76
111
|
end
|
77
112
|
|
113
|
+
it 'return false when no If-None-Match is sent, but an ETag is set' do
|
114
|
+
@controller = dispatch_to(Merb::Test::Fixtures::Controllers::ConditionalGet,
|
115
|
+
:sets_etag, {}, {})
|
116
|
+
|
117
|
+
@controller.request_fresh?.should be(false)
|
118
|
+
end
|
119
|
+
|
78
120
|
it 'return true when entity is not modified since date given in request header' do
|
79
121
|
env = { Merb::Const::HTTP_IF_MODIFIED_SINCE => Time.at(7000).httpdate }
|
80
122
|
@controller = dispatch_to(Merb::Test::Fixtures::Controllers::ConditionalGet,
|
81
123
|
:sets_last_modified, {}, env)
|
82
|
-
|
124
|
+
|
83
125
|
@controller.request_fresh?.should be(true)
|
84
126
|
end
|
85
127
|
|
128
|
+
it 'return false when a Last-Modified is set, but no header is sent' do
|
129
|
+
@controller = dispatch_to(Merb::Test::Fixtures::Controllers::ConditionalGet,
|
130
|
+
:sets_last_modified, {}, {})
|
131
|
+
|
132
|
+
@controller.request_fresh?.should be(false)
|
133
|
+
end
|
134
|
+
|
86
135
|
it 'return true when both etag and last modification date satisfy request freshness' do
|
87
|
-
env = { 'HTTP_IF_MODIFIED_SINCE' => Time.at(7000).httpdate, Merb::Const::HTTP_IF_NONE_MATCH => '"39791e6fb09"' }
|
136
|
+
env = { 'HTTP_IF_MODIFIED_SINCE' => Time.at(7000).httpdate, Merb::Const::HTTP_IF_NONE_MATCH => '"39791e6fb09"' }
|
88
137
|
@controller = dispatch_to(Merb::Test::Fixtures::Controllers::ConditionalGet,
|
89
138
|
:superfresh, {}, env)
|
90
|
-
|
139
|
+
|
91
140
|
@controller.request_fresh?.should be(true)
|
92
141
|
end
|
93
142
|
|
143
|
+
it 'return false if etag satisfies but the last modification date does not satisfy request freshness' do
|
144
|
+
env = { 'HTTP_IF_MODIFIED_SINCE' => Time.at(6999).httpdate, Merb::Const::HTTP_IF_NONE_MATCH => '"39791e6fb09"' }
|
145
|
+
@controller = dispatch_to(Merb::Test::Fixtures::Controllers::ConditionalGet,
|
146
|
+
:superfresh, {}, env)
|
147
|
+
|
148
|
+
@controller.request_fresh?.should be(false)
|
149
|
+
end
|
150
|
+
|
151
|
+
it 'return false if etag does not satisfy but the last modification date does satisfy request freshness' do
|
152
|
+
env = { 'HTTP_IF_MODIFIED_SINCE' => Time.at(7000).httpdate, Merb::Const::HTTP_IF_NONE_MATCH => '"wrong"' }
|
153
|
+
@controller = dispatch_to(Merb::Test::Fixtures::Controllers::ConditionalGet,
|
154
|
+
:superfresh, {}, env)
|
155
|
+
|
156
|
+
@controller.request_fresh?.should be(false)
|
157
|
+
end
|
158
|
+
|
94
159
|
it 'return false when neither etag nor last modification date satisfy request freshness' do
|
95
|
-
env = { 'HTTP_IF_MODIFIED_SINCE' => Time.at(7000).httpdate, Merb::Const::HTTP_IF_NONE_MATCH => '"39791e6fb09"' }
|
160
|
+
env = { 'HTTP_IF_MODIFIED_SINCE' => Time.at(7000).httpdate, Merb::Const::HTTP_IF_NONE_MATCH => '"39791e6fb09"' }
|
96
161
|
@controller = dispatch_to(Merb::Test::Fixtures::Controllers::ConditionalGet,
|
97
162
|
:stale, {}, env)
|
98
|
-
|
163
|
+
|
99
164
|
@controller.request_fresh?.should be(false)
|
100
|
-
end
|
165
|
+
end
|
101
166
|
end
|
@@ -21,6 +21,18 @@ module Merb::Test::Fixtures::Controllers
|
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
24
|
+
class PermanentAndStatusRedirect < Testing
|
25
|
+
def index
|
26
|
+
redirect("/", :permanent => true, :status => 302)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
class WithStatusRedirect < Testing
|
31
|
+
def index
|
32
|
+
redirect("/", :status => 307)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
24
36
|
class RedirectWithMessage < Testing
|
25
37
|
def index
|
26
38
|
redirect("/", :message => { :notice => "what?" })
|
@@ -57,4 +69,4 @@ module Merb::Test::Fixtures::Controllers
|
|
57
69
|
message[:notice]
|
58
70
|
end
|
59
71
|
end
|
60
|
-
end
|
72
|
+
end
|
@@ -19,6 +19,18 @@ describe Merb::Controller, " redirects" do
|
|
19
19
|
@controller.headers["Location"].should == "/"
|
20
20
|
end
|
21
21
|
|
22
|
+
it "recirect with :permanent and :stauts use :status" do
|
23
|
+
@controller = dispatch_to(Merb::Test::Fixtures::Controllers::PermanentAndStatusRedirect, :index)
|
24
|
+
@controller.status.should == 302
|
25
|
+
@controller.headers["Location"].should == "/"
|
26
|
+
end
|
27
|
+
|
28
|
+
it "redirect with status" do
|
29
|
+
@controller = dispatch_to(Merb::Test::Fixtures::Controllers::WithStatusRedirect, :index)
|
30
|
+
@controller.status.should == 307
|
31
|
+
@controller.headers["Location"].should == "/"
|
32
|
+
end
|
33
|
+
|
22
34
|
it "redirects with messages" do
|
23
35
|
@controller = dispatch_to(Merb::Test::Fixtures::Controllers::RedirectWithMessage, :index)
|
24
36
|
@controller.status.should == 302
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "spec_helper"))
|
2
|
+
|
3
|
+
describe Merb::Config do
|
4
|
+
|
5
|
+
it "should set Dispatcher.use_mutex to true by default" do
|
6
|
+
lambda {
|
7
|
+
startup_merb
|
8
|
+
Merb::Dispatcher.use_mutex.should be_true
|
9
|
+
}
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should set Dispatcher.use_mutex to value in config" do
|
13
|
+
lambda {
|
14
|
+
startup_merb({:use_mutex => false})
|
15
|
+
Merb::Dispatcher.use_mutex.should be_false
|
16
|
+
}
|
17
|
+
end
|
18
|
+
end
|
@@ -1,13 +1,23 @@
|
|
1
1
|
require File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "spec_helper"))
|
2
2
|
startup_merb
|
3
3
|
|
4
|
+
module Merb::MultipartRequestSpecHelper
|
5
|
+
def fake_file(read = nil, filename = 'sample.txt', path = 'sample.txt')
|
6
|
+
read ||= 'This is a text file with some small content in it.'
|
7
|
+
Struct.new(:read, :filename, :path).new(read, filename, path)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
4
11
|
describe Merb::Request do
|
12
|
+
include Merb::MultipartRequestSpecHelper
|
13
|
+
|
5
14
|
it "should handle file upload for multipart/form-data posts" do
|
6
|
-
file =
|
7
|
-
|
8
|
-
m = Merb::Test::MultipartRequestHelper::Post.new :file => file
|
15
|
+
file = fake_file
|
16
|
+
m = Merb::Test::MultipartRequestHelper::Post.new(:file => file)
|
9
17
|
body, head = m.to_multipart
|
10
|
-
request = fake_request({:request_method => "POST",
|
18
|
+
request = fake_request({:request_method => "POST",
|
19
|
+
:content_type => head,
|
20
|
+
:content_length => body.length}, :req => body)
|
11
21
|
request.params[:file].should_not be_nil
|
12
22
|
request.params[:file][:tempfile].class.should == Tempfile
|
13
23
|
request.params[:file][:content_type].should == 'text/plain'
|
@@ -15,23 +25,22 @@ describe Merb::Request do
|
|
15
25
|
end
|
16
26
|
|
17
27
|
it "should correctly format multipart posts which contain multiple parameters" do
|
18
|
-
|
19
|
-
new("This is a text file with some small content in it.", "sample.txt", "sample.txt")
|
20
|
-
params = {:model => {:description1 => 'foo', :description2 => 'bar', :file => file}}
|
21
|
-
|
28
|
+
params = {:model => {:description1 => 'foo', :description2 => 'bar', :file => fake_file}}
|
22
29
|
m = Merb::Test::MultipartRequestHelper::Post.new params
|
23
30
|
body, head = m.to_multipart
|
24
31
|
body.split('----------0xKhTmLbOuNdArY').size.should eql(5)
|
25
32
|
end
|
26
33
|
|
27
34
|
it "should correctly format multipart posts which contain an array as parameter" do
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
+
file = fake_file
|
36
|
+
file2 = fake_file("This is another text file", "sample2.txt", "sample2.txt")
|
37
|
+
params = {:model => {:description1 => 'foo',
|
38
|
+
:description2 => 'bar',
|
39
|
+
:child_attributes => [
|
40
|
+
{ :file => file },
|
41
|
+
{ :file => file2 }
|
42
|
+
]
|
43
|
+
}}
|
35
44
|
|
36
45
|
m = Merb::Test::MultipartRequestHelper::Post.new params
|
37
46
|
body, head = m.to_multipart
|
@@ -42,8 +51,7 @@ describe Merb::Request do
|
|
42
51
|
end
|
43
52
|
|
44
53
|
it "should accept env['rack.input'] as IO object (instead of StringIO)" do
|
45
|
-
file =
|
46
|
-
new("This is a text file with some small content in it.", "sample.txt", "sample.txt")
|
54
|
+
file = fake_file
|
47
55
|
m = Merb::Test::MultipartRequestHelper::Post.new :file => file
|
48
56
|
body, head = m.to_multipart
|
49
57
|
|
@@ -66,5 +74,16 @@ describe Merb::Request do
|
|
66
74
|
request = fake_request({:request_method => "GET", :content_type => 'multipart/form-data, boundary=----------0xKhTmLbOuNdArY', :content_length => 0}, :req => '')
|
67
75
|
running {request.params}.should_not raise_error
|
68
76
|
end
|
69
|
-
|
77
|
+
|
78
|
+
it "should handle multiple occurences of one parameter" do
|
79
|
+
m = Merb::Test::MultipartRequestHelper::Post.new :file => fake_file
|
80
|
+
m.push_params({:checkbox => 0})
|
81
|
+
m.push_params({:checkbox => 1})
|
82
|
+
body, head = m.to_multipart
|
83
|
+
request = fake_request({:request_method => "POST",
|
84
|
+
:content_type => head,
|
85
|
+
:content_length => body.length}, :req => body)
|
86
|
+
request.params[:file].should_not be_nil
|
87
|
+
request.params[:checkbox].should eql '1'
|
88
|
+
end
|
70
89
|
end
|
@@ -2,6 +2,36 @@ require File.join(File.dirname(__FILE__), "spec_helper")
|
|
2
2
|
startup_merb(:session_store => "memory")
|
3
3
|
require File.join(File.dirname(__FILE__), "controllers", "sessions")
|
4
4
|
|
5
|
+
describe Merb::MemorySession, "container" do
|
6
|
+
|
7
|
+
it "should always generate unique session" do
|
8
|
+
# Fix session id generation
|
9
|
+
Merb::SessionMixin.stub!(:rand_uuid).and_return(1, 1, 2)
|
10
|
+
|
11
|
+
s1 = Merb::MemorySession.generate
|
12
|
+
s1.store.store_session(s1.session_id, {:foo => 'bar'})
|
13
|
+
s1.session_id.should eql 1
|
14
|
+
|
15
|
+
s2 = Merb::MemorySession.generate
|
16
|
+
s2.session_id.should eql 2
|
17
|
+
# Cleanup
|
18
|
+
s1.store.delete_session(1)
|
19
|
+
s2.store.delete_session(2)
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should raise exception if unable to generate unique ID" do
|
23
|
+
# Fix session id generation
|
24
|
+
Merb::SessionMixin.stub!(:rand_uuid).and_return(1, 1)
|
25
|
+
|
26
|
+
s1 = Merb::MemorySession.generate
|
27
|
+
s1.store.store_session(s1.session_id, {:foo => 'bar'})
|
28
|
+
|
29
|
+
lambda { s2 = Merb::MemorySession.generate }.should raise_error
|
30
|
+
|
31
|
+
s1.store.delete_session(1)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
5
35
|
describe Merb::MemorySession do
|
6
36
|
|
7
37
|
before do
|
@@ -23,4 +53,4 @@ describe Merb::MemorySession, "mixed into Merb::Controller" do
|
|
23
53
|
|
24
54
|
it_should_behave_like "All session-stores mixed into Merb::Controller"
|
25
55
|
|
26
|
-
end
|
56
|
+
end
|
metadata
CHANGED
@@ -1,13 +1,12 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: merb-core
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
prerelease:
|
4
|
+
prerelease: false
|
5
5
|
segments:
|
6
6
|
- 1
|
7
7
|
- 1
|
8
8
|
- 0
|
9
|
-
|
10
|
-
version: 1.1.0.rc1
|
9
|
+
version: 1.1.0
|
11
10
|
platform: ruby
|
12
11
|
authors:
|
13
12
|
- Ezra Zygmuntowicz
|
@@ -15,7 +14,7 @@ autorequire:
|
|
15
14
|
bindir: bin
|
16
15
|
cert_chain: []
|
17
16
|
|
18
|
-
date: 2010-03-
|
17
|
+
date: 2010-03-22 00:00:00 +00:00
|
19
18
|
default_executable: merb
|
20
19
|
dependencies:
|
21
20
|
- !ruby/object:Gem::Dependency
|
@@ -388,6 +387,7 @@ files:
|
|
388
387
|
- spec/public/controller/spec_helper.rb
|
389
388
|
- spec/public/controller/streaming_spec.rb
|
390
389
|
- spec/public/controller/url_spec.rb
|
390
|
+
- spec/public/core/config_spec.rb
|
391
391
|
- spec/public/core/merb_core_spec.rb
|
392
392
|
- spec/public/core_ext/kernel_spec.rb
|
393
393
|
- spec/public/core_ext/spec_helper.rb
|
@@ -2400,13 +2400,11 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
2400
2400
|
version: "0"
|
2401
2401
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
2402
2402
|
requirements:
|
2403
|
-
- - "
|
2403
|
+
- - ">="
|
2404
2404
|
- !ruby/object:Gem::Version
|
2405
2405
|
segments:
|
2406
|
-
-
|
2407
|
-
|
2408
|
-
- 1
|
2409
|
-
version: 1.3.1
|
2406
|
+
- 0
|
2407
|
+
version: "0"
|
2410
2408
|
requirements: []
|
2411
2409
|
|
2412
2410
|
rubyforge_project:
|