merb-core 0.9.2 → 0.9.3
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/Rakefile +61 -11
- data/bin/merb +5 -1
- data/lib/merb-core.rb +202 -25
- data/lib/merb-core/autoload.rb +19 -17
- data/lib/merb-core/bootloader.rb +84 -71
- data/lib/merb-core/config.rb +19 -14
- data/lib/merb-core/controller/abstract_controller.rb +16 -17
- data/lib/merb-core/controller/exceptions.rb +115 -70
- data/lib/merb-core/controller/merb_controller.rb +62 -38
- data/lib/merb-core/controller/mime.rb +1 -1
- data/lib/merb-core/controller/mixins/authentication.rb +87 -0
- data/lib/merb-core/controller/mixins/controller.rb +16 -15
- data/lib/merb-core/controller/mixins/render.rb +113 -19
- data/lib/merb-core/controller/mixins/responder.rb +8 -2
- data/lib/merb-core/controller/template.rb +1 -1
- data/lib/merb-core/core_ext.rb +1 -0
- data/lib/merb-core/core_ext/class.rb +113 -6
- data/lib/merb-core/core_ext/hash.rb +43 -39
- data/lib/merb-core/core_ext/kernel.rb +75 -38
- data/lib/merb-core/core_ext/mash.rb +4 -4
- data/lib/merb-core/core_ext/object.rb +18 -7
- data/lib/merb-core/core_ext/set.rb +9 -4
- data/lib/merb-core/core_ext/string.rb +29 -9
- data/lib/merb-core/core_ext/time.rb +13 -0
- data/lib/merb-core/dispatch/cookies.rb +1 -2
- data/lib/merb-core/dispatch/dispatcher.rb +18 -10
- data/lib/merb-core/dispatch/exceptions.html.erb +1 -1
- data/lib/merb-core/dispatch/request.rb +3 -0
- data/lib/merb-core/dispatch/router.rb +10 -7
- data/lib/merb-core/dispatch/router/behavior.rb +36 -27
- data/lib/merb-core/dispatch/router/route.rb +7 -2
- data/lib/merb-core/dispatch/session/cookie.rb +4 -4
- data/lib/merb-core/dispatch/session/memcached.rb +17 -5
- data/lib/merb-core/logger.rb +2 -2
- data/lib/merb-core/plugins.rb +16 -4
- data/lib/merb-core/rack/adapter/ebb.rb +4 -1
- data/lib/merb-core/rack/adapter/evented_mongrel.rb +2 -0
- data/lib/merb-core/rack/adapter/fcgi.rb +1 -0
- data/lib/merb-core/rack/adapter/mongrel.rb +1 -0
- data/lib/merb-core/rack/adapter/runner.rb +1 -0
- data/lib/merb-core/rack/adapter/thin.rb +3 -1
- data/lib/merb-core/rack/adapter/webrick.rb +1 -0
- data/lib/merb-core/rack/application.rb +17 -1
- data/lib/merb-core/server.rb +78 -28
- data/lib/merb-core/test/helpers/multipart_request_helper.rb +3 -3
- data/lib/merb-core/test/helpers/request_helper.rb +81 -27
- data/lib/merb-core/test/helpers/view_helper.rb +1 -1
- data/lib/merb-core/test/matchers/controller_matchers.rb +55 -5
- data/lib/merb-core/test/matchers/route_matchers.rb +8 -17
- data/lib/merb-core/test/matchers/view_matchers.rb +53 -11
- data/lib/merb-core/test/run_specs.rb +22 -14
- data/lib/merb-core/test/tasks/spectasks.rb +54 -33
- data/lib/merb-core/vendor/facets/inflect.rb +91 -2
- data/lib/merb-core/version.rb +2 -2
- data/spec/private/config/config_spec.rb +54 -26
- data/spec/private/core_ext/class_spec.rb +22 -0
- data/spec/private/core_ext/hash_spec.rb +70 -54
- data/spec/private/core_ext/kernel_spec.rb +149 -14
- data/spec/private/core_ext/object_spec.rb +92 -10
- data/spec/private/core_ext/string_spec.rb +162 -4
- data/spec/private/core_ext/time_spec.rb +16 -0
- data/spec/private/dispatch/bootloader_spec.rb +24 -0
- data/spec/private/dispatch/fixture/app/views/exeptions/client_error.html.erb +1 -1
- data/spec/private/dispatch/fixture/app/views/exeptions/internal_server_error.html.erb +1 -1
- data/spec/private/dispatch/fixture/app/views/exeptions/not_acceptable.html.erb +1 -1
- data/spec/private/dispatch/fixture/app/views/exeptions/not_found.html.erb +1 -1
- data/spec/private/dispatch/fixture/config/black_hole.rb +12 -0
- data/spec/private/dispatch/fixture/log/merb_test.log +138 -0
- data/spec/private/plugins/plugin_spec.rb +79 -8
- data/spec/private/rack/application_spec.rb +1 -1
- data/spec/public/abstract_controller/controllers/filters.rb +26 -0
- data/spec/public/abstract_controller/controllers/helpers.rb +2 -2
- data/spec/public/abstract_controller/controllers/partial.rb +2 -2
- data/spec/public/abstract_controller/controllers/render.rb +16 -4
- data/spec/public/abstract_controller/filter_spec.rb +8 -0
- data/spec/public/abstract_controller/render_spec.rb +12 -0
- data/spec/public/controller/authentication_spec.rb +103 -0
- data/spec/public/controller/base_spec.rb +4 -3
- data/spec/public/controller/controllers/authentication.rb +47 -0
- data/spec/public/controller/controllers/base.rb +1 -0
- data/spec/public/controller/controllers/display.rb +30 -0
- data/spec/public/controller/controllers/views/layout/custom_arg.html.erb +1 -0
- data/spec/public/controller/controllers/views/merb/test/fixtures/controllers/display_with_template_argument/index.html.erb +1 -0
- data/spec/public/controller/display_spec.rb +17 -0
- data/spec/public/controller/spec_helper.rb +1 -0
- data/spec/public/controller/url_spec.rb +25 -7
- data/spec/public/core/merb_core_spec.rb +34 -0
- data/spec/public/directory_structure/directory/app/controllers/custom.rb +2 -2
- data/spec/public/directory_structure/directory/log/merb_test.log +48 -0
- data/spec/public/logger/logger_spec.rb +10 -4
- data/spec/public/reloading/directory/app/controllers/reload.rb +1 -1
- data/spec/public/reloading/directory/log/merb_test.log +13 -0
- data/spec/public/reloading/reload_spec.rb +23 -22
- data/spec/public/request/request_spec.rb +2 -0
- data/spec/public/router/nested_resources_spec.rb +7 -0
- data/spec/public/router/resources_spec.rb +46 -1
- data/spec/public/router/special_spec.rb +5 -1
- data/spec/public/test/controller_matchers_spec.rb +25 -1
- data/spec/public/test/controllers/spec_helper_controller.rb +8 -0
- data/spec/public/test/request_helper_spec.rb +52 -1
- data/spec/public/test/route_matchers_spec.rb +27 -25
- data/spec/public/test/view_helper_spec.rb +1 -1
- data/spec/public/test/view_matchers_spec.rb +148 -72
- metadata +23 -3
|
@@ -1,22 +1,23 @@
|
|
|
1
1
|
class Merb::Controller < Merb::AbstractController
|
|
2
|
-
|
|
2
|
+
|
|
3
3
|
class_inheritable_accessor :_hidden_actions, :_shown_actions
|
|
4
4
|
cattr_accessor :_subclasses, :_session_id_key, :_session_secret_key, :_session_expiry
|
|
5
5
|
self._subclasses = Set.new
|
|
6
6
|
|
|
7
7
|
def self.subclasses_list() _subclasses end
|
|
8
|
-
|
|
8
|
+
|
|
9
9
|
self._session_secret_key = nil
|
|
10
10
|
self._session_id_key = Merb::Config[:session_id_key] || '_session_id'
|
|
11
11
|
self._session_expiry = Merb::Config[:session_expiry] || Merb::Const::WEEK * 2
|
|
12
|
-
|
|
12
|
+
|
|
13
13
|
include Merb::ResponderMixin
|
|
14
14
|
include Merb::ControllerMixin
|
|
15
|
+
include Merb::AuthenticationMixin
|
|
15
16
|
|
|
16
17
|
attr_accessor :route
|
|
17
|
-
|
|
18
|
+
|
|
18
19
|
class << self
|
|
19
|
-
|
|
20
|
+
|
|
20
21
|
# ==== Parameters
|
|
21
22
|
# klass<Merb::Controller>::
|
|
22
23
|
# The Merb::Controller inheriting from the base class.
|
|
@@ -34,7 +35,7 @@ class Merb::Controller < Merb::AbstractController
|
|
|
34
35
|
# ==== Returns
|
|
35
36
|
# Array[String]::
|
|
36
37
|
# An array of actions that should not be possible to dispatch to.
|
|
37
|
-
#
|
|
38
|
+
#
|
|
38
39
|
#---
|
|
39
40
|
# @public
|
|
40
41
|
def hide_action(*names)
|
|
@@ -51,7 +52,7 @@ class Merb::Controller < Merb::AbstractController
|
|
|
51
52
|
# Array[String]::
|
|
52
53
|
# An array of actions that should be dispatched to even if they would not
|
|
53
54
|
# otherwise be.
|
|
54
|
-
#
|
|
55
|
+
#
|
|
55
56
|
# ==== Example
|
|
56
57
|
# module Foo
|
|
57
58
|
# def self.included(base)
|
|
@@ -74,7 +75,7 @@ class Merb::Controller < Merb::AbstractController
|
|
|
74
75
|
end
|
|
75
76
|
|
|
76
77
|
# This list of actions that should not be callable.
|
|
77
|
-
#
|
|
78
|
+
#
|
|
78
79
|
# ==== Returns
|
|
79
80
|
# Array[String]:: An array of actions that should not be dispatchable.
|
|
80
81
|
def _hidden_actions
|
|
@@ -83,14 +84,14 @@ class Merb::Controller < Merb::AbstractController
|
|
|
83
84
|
end
|
|
84
85
|
|
|
85
86
|
# This list of actions that should be callable.
|
|
86
|
-
#
|
|
87
|
+
#
|
|
87
88
|
# ==== Returns
|
|
88
89
|
# Array[String]::
|
|
89
90
|
# An array of actions that should be dispatched to even if they would not
|
|
90
91
|
# otherwise be.
|
|
91
92
|
def _shown_actions
|
|
92
93
|
actions = read_inheritable_attribute(:_shown_actions)
|
|
93
|
-
actions ? actions : write_inheritable_attribute(:_shown_actions, [])
|
|
94
|
+
actions ? actions : write_inheritable_attribute(:_shown_actions, [])
|
|
94
95
|
end
|
|
95
96
|
|
|
96
97
|
# The list of actions that are callable, after taking defaults,
|
|
@@ -98,7 +99,7 @@ class Merb::Controller < Merb::AbstractController
|
|
|
98
99
|
# once, the first time an action is dispatched for this controller.
|
|
99
100
|
#
|
|
100
101
|
# ==== Returns
|
|
101
|
-
#
|
|
102
|
+
# SimpleSet[String]:: A set of actions that should be callable.
|
|
102
103
|
def callable_actions
|
|
103
104
|
unless @callable_actions
|
|
104
105
|
callables = []
|
|
@@ -106,40 +107,53 @@ class Merb::Controller < Merb::AbstractController
|
|
|
106
107
|
begin
|
|
107
108
|
callables << (klass.public_instance_methods(false) + klass._shown_actions) - klass._hidden_actions
|
|
108
109
|
klass = klass.superclass
|
|
109
|
-
end until klass == Merb::
|
|
110
|
+
end until klass == Merb::AbstractController || klass == Object
|
|
110
111
|
@callable_actions = Merb::SimpleSet.new(callables.flatten)
|
|
111
112
|
end
|
|
112
113
|
@callable_actions
|
|
113
114
|
end
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
115
|
+
|
|
116
|
+
# This is a stub method so plugins can implement param filtering if they want.
|
|
117
|
+
#
|
|
118
|
+
# ==== Parameters
|
|
119
|
+
# params<Hash{Symbol => String}>:: A list of params
|
|
120
|
+
#
|
|
121
|
+
# ==== Returns
|
|
122
|
+
# Hash{Symbol => String}:: A new list of params, filtered as desired
|
|
123
|
+
#---
|
|
124
|
+
# @semipublic
|
|
125
|
+
def _filter_params(params)
|
|
126
|
+
params
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
end # class << self
|
|
130
|
+
|
|
131
|
+
# The location to look for a template for a particular controller, context,
|
|
118
132
|
# and mime-type. This is overridden from AbstractController, which defines a
|
|
119
133
|
# version of this that does not involve mime-types.
|
|
120
134
|
#
|
|
121
135
|
# ==== Parameters
|
|
122
|
-
#
|
|
136
|
+
# context<~to_s>:: The name of the action or template basename that will be rendered.
|
|
123
137
|
# type<~to_s>::
|
|
124
138
|
# The mime-type of the template that will be rendered. Defaults to nil.
|
|
125
139
|
# controller<~to_s>::
|
|
126
140
|
# The name of the controller that will be rendered. Defaults to
|
|
127
141
|
# controller_name.
|
|
128
142
|
#
|
|
129
|
-
# ====
|
|
143
|
+
# ==== Notes
|
|
130
144
|
# By default, this renders ":controller/:action.:type". To change this,
|
|
131
145
|
# override it in your application class or in individual controllers.
|
|
132
146
|
#
|
|
133
147
|
#---
|
|
134
148
|
# @public
|
|
135
|
-
def _template_location(
|
|
136
|
-
"#{controller}/#{
|
|
137
|
-
end
|
|
138
|
-
|
|
149
|
+
def _template_location(context, type = nil, controller = controller_name)
|
|
150
|
+
controller ? "#{controller}/#{context}.#{type}" : "#{context}.#{type}"
|
|
151
|
+
end
|
|
152
|
+
|
|
139
153
|
# Build a new controller.
|
|
140
154
|
#
|
|
141
155
|
# Sets the variables that came in through the dispatch as available to
|
|
142
|
-
# the controller.
|
|
156
|
+
# the controller.
|
|
143
157
|
#
|
|
144
158
|
# This method uses the :session_id_cookie_only and :query_string_whitelist
|
|
145
159
|
# configuration options. See CONFIG for more details.
|
|
@@ -147,7 +161,7 @@ class Merb::Controller < Merb::AbstractController
|
|
|
147
161
|
# ==== Parameters
|
|
148
162
|
# request<Merb::Request>:: The Merb::Request that came in from Mongrel.
|
|
149
163
|
# status<Integer>:: An integer code for the status. Defaults to 200.
|
|
150
|
-
# headers<Hash{header => value}>::
|
|
164
|
+
# headers<Hash{header => value}>::
|
|
151
165
|
# A hash of headers to start the controller with. These headers can be
|
|
152
166
|
# overridden later by the #headers method.
|
|
153
167
|
#---
|
|
@@ -156,7 +170,7 @@ class Merb::Controller < Merb::AbstractController
|
|
|
156
170
|
super()
|
|
157
171
|
@request, @status, @headers = request, status, headers
|
|
158
172
|
end
|
|
159
|
-
|
|
173
|
+
|
|
160
174
|
# Dispatch the action.
|
|
161
175
|
#
|
|
162
176
|
# ==== Parameters
|
|
@@ -178,37 +192,47 @@ class Merb::Controller < Merb::AbstractController
|
|
|
178
192
|
end
|
|
179
193
|
@_benchmarks[:action_time] = Time.now - start
|
|
180
194
|
end
|
|
181
|
-
|
|
195
|
+
|
|
182
196
|
attr_reader :request, :headers
|
|
183
|
-
|
|
184
|
-
|
|
197
|
+
attr_reader :status
|
|
198
|
+
|
|
199
|
+
# Set the response status code.
|
|
200
|
+
#
|
|
201
|
+
# ==== Parameters
|
|
202
|
+
# s<Fixnum, Symbol>:: A status-code or named http-status
|
|
203
|
+
def status=(s)
|
|
204
|
+
if s.is_a?(Symbol) && STATUS_CODES.key?(s)
|
|
205
|
+
@status = STATUS_CODES[s]
|
|
206
|
+
elsif s.is_a?(Fixnum)
|
|
207
|
+
@status = s
|
|
208
|
+
else
|
|
209
|
+
raise ArgumentError, "Status should be of type Fixnum or Symbol, was #{s.class}"
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
|
|
185
213
|
# ==== Returns
|
|
186
214
|
# Hash:: The parameters from the request object
|
|
187
215
|
def params() request.params end
|
|
188
|
-
|
|
216
|
+
|
|
189
217
|
# ==== Returns
|
|
190
|
-
# Merb::Cookies::
|
|
218
|
+
# Merb::Cookies::
|
|
191
219
|
# A new Merb::Cookies instance representing the cookies that came in
|
|
192
220
|
# from the request object
|
|
193
221
|
#
|
|
194
|
-
# ====
|
|
222
|
+
# ==== Notes
|
|
195
223
|
# Headers are passed into the cookie object so that you can do:
|
|
196
224
|
# cookies[:foo] = "bar"
|
|
197
225
|
def cookies() @_cookies ||= _setup_cookies end
|
|
198
|
-
|
|
226
|
+
|
|
199
227
|
# ==== Returns
|
|
200
228
|
# Hash:: The session that was extracted from the request object.
|
|
201
229
|
def session() request.session end
|
|
202
|
-
|
|
230
|
+
|
|
203
231
|
private
|
|
204
232
|
|
|
205
233
|
# Create a default cookie jar, and pre-set a fixation cookie
|
|
206
234
|
# if fixation is enabled
|
|
207
235
|
def _setup_cookies
|
|
208
|
-
|
|
209
|
-
if request.params.key?(_session_id_key) && route.allow_fixation?
|
|
210
|
-
cookies[_session_id_key] = request.params[_session_id_key]
|
|
211
|
-
end
|
|
212
|
-
cookies
|
|
236
|
+
::Merb::Cookies.new(request.cookies, @headers)
|
|
213
237
|
end
|
|
214
238
|
end
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
module Merb::AuthenticationMixin
|
|
2
|
+
|
|
3
|
+
# Attempts to authenticate the user via HTTP Basic authentication. Takes a
|
|
4
|
+
# block with the username and password, if the block yields false the
|
|
5
|
+
# authentication is not accepted and :halt is thrown.
|
|
6
|
+
#
|
|
7
|
+
# If no block is passed, +basic_authentication+, the +request+ and +authenticate+
|
|
8
|
+
# methods can be chained. These can be used to independently request authentication
|
|
9
|
+
# or confirm it, if more control is desired.
|
|
10
|
+
#
|
|
11
|
+
# ==== Parameters
|
|
12
|
+
# realm<~to_s>:: The realm to authenticate against. Defaults to 'Application'.
|
|
13
|
+
# &authenticator:: A block to check if the authentication is valid.
|
|
14
|
+
#
|
|
15
|
+
# ==== Examples
|
|
16
|
+
# class Application < Merb::Controller
|
|
17
|
+
#
|
|
18
|
+
# before :authenticate
|
|
19
|
+
#
|
|
20
|
+
# protected
|
|
21
|
+
#
|
|
22
|
+
# def authenticate
|
|
23
|
+
# basic_authentication("My App") do |username, password|
|
|
24
|
+
# password == "secret"
|
|
25
|
+
# end
|
|
26
|
+
# end
|
|
27
|
+
#
|
|
28
|
+
# end
|
|
29
|
+
#
|
|
30
|
+
# class Application < Merb::Controller
|
|
31
|
+
#
|
|
32
|
+
# before :authenticate
|
|
33
|
+
#
|
|
34
|
+
# def authenticate
|
|
35
|
+
# user = basic_authentication.authenticate do |username, password|
|
|
36
|
+
# User.authenticate(username, password)
|
|
37
|
+
# end
|
|
38
|
+
#
|
|
39
|
+
# if user
|
|
40
|
+
# @current_user = user
|
|
41
|
+
# else
|
|
42
|
+
# basic_authentication.request
|
|
43
|
+
# end
|
|
44
|
+
# end
|
|
45
|
+
#
|
|
46
|
+
# end
|
|
47
|
+
#
|
|
48
|
+
#---
|
|
49
|
+
# @public
|
|
50
|
+
def basic_authentication(realm = "Application", &authenticator)
|
|
51
|
+
BasicAuthentication.new(self, realm, &authenticator)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
class BasicAuthentication #:nodoc:
|
|
55
|
+
# So we can have access to the status codes
|
|
56
|
+
include Merb::ControllerExceptions
|
|
57
|
+
|
|
58
|
+
def initialize(controller, realm = "Application", &authenticator)
|
|
59
|
+
@controller = controller
|
|
60
|
+
@realm = realm
|
|
61
|
+
authenticate_or_request(&authenticator) if authenticator
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def authenticate(&authenticator)
|
|
65
|
+
auth = Rack::Auth::Basic::Request.new(@controller.request.env)
|
|
66
|
+
|
|
67
|
+
if auth.provided? and auth.basic?
|
|
68
|
+
authenticator.call(*auth.credentials)
|
|
69
|
+
else
|
|
70
|
+
false
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def request
|
|
75
|
+
@controller.headers['WWW-Authenticate'] = 'Basic realm="%s"' % @realm
|
|
76
|
+
throw :halt, @controller.render("HTTP Basic: Access denied.\n", :status => Unauthorized.status, :layout => false)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
protected
|
|
80
|
+
|
|
81
|
+
def authenticate_or_request(&authenticator)
|
|
82
|
+
authenticate(&authenticator) || request
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
end
|
|
@@ -186,15 +186,17 @@ module Merb
|
|
|
186
186
|
opts.update(Merb::Const::DEFAULT_SEND_FILE_OPTIONS.merge(opts))
|
|
187
187
|
disposition = opts[:disposition].dup || 'attachment'
|
|
188
188
|
disposition << %(; filename="#{opts[:filename]}")
|
|
189
|
-
|
|
189
|
+
headers.update(
|
|
190
190
|
'Content-Type' => opts[:type].strip, # fixes a problem with extra '\r' with some browsers
|
|
191
191
|
'Content-Disposition' => disposition,
|
|
192
192
|
'Content-Transfer-Encoding' => 'binary',
|
|
193
193
|
'CONTENT-LENGTH' => opts[:content_length]
|
|
194
194
|
)
|
|
195
|
-
response
|
|
196
|
-
|
|
197
|
-
|
|
195
|
+
Proc.new{|response|
|
|
196
|
+
response.send_status(opts[:content_length])
|
|
197
|
+
response.send_header
|
|
198
|
+
stream.call(response)
|
|
199
|
+
}
|
|
198
200
|
end
|
|
199
201
|
|
|
200
202
|
# Uses the nginx specific +X-Accel-Redirect+ header to send a file directly
|
|
@@ -208,22 +210,19 @@ module Merb
|
|
|
208
210
|
return
|
|
209
211
|
end
|
|
210
212
|
|
|
211
|
-
# Sets a cookie to be included in the response.
|
|
212
|
-
# primarily internally in Merb.
|
|
213
|
+
# Sets a cookie to be included in the response.
|
|
213
214
|
#
|
|
214
215
|
# If you need to set a cookie, then use the +cookies+ hash.
|
|
215
216
|
#
|
|
216
217
|
# ==== Parameters
|
|
217
218
|
# name<~to_s>:: A name for the cookie.
|
|
218
219
|
# value<~to_s>:: A value for the cookie.
|
|
219
|
-
# expires<~gmtime:~strftime>:: An expiration time for the cookie.
|
|
220
|
+
# expires<~gmtime:~strftime, Hash>:: An expiration time for the cookie, or a hash of cookie options.
|
|
221
|
+
# ---
|
|
222
|
+
# @public
|
|
220
223
|
def set_cookie(name, value, expires)
|
|
221
|
-
(
|
|
222
|
-
|
|
223
|
-
::Merb::Request.escape(value.to_s),
|
|
224
|
-
# Cookie expiration time must be GMT. See RFC 2109
|
|
225
|
-
expires.gmtime.strftime(Merb::Const::COOKIE_EXPIRATION_FORMAT)
|
|
226
|
-
])
|
|
224
|
+
options = expires.is_a?(Hash) ? expires : {:expires => expires}
|
|
225
|
+
cookies.set_cookie(name, value, options)
|
|
227
226
|
end
|
|
228
227
|
|
|
229
228
|
# Marks a cookie as deleted and gives it an expires stamp in the past. This
|
|
@@ -256,7 +255,9 @@ module Merb
|
|
|
256
255
|
# ==== Raises
|
|
257
256
|
# NotImplemented:: The Rack adapter doens't support streaming.
|
|
258
257
|
def must_support_streaming!
|
|
259
|
-
|
|
258
|
+
unless request.env['rack.streaming']
|
|
259
|
+
raise(Merb::ControllerExceptions::NotImplemented, "Current Rack adapter does not support streaming")
|
|
260
|
+
end
|
|
260
261
|
end
|
|
261
262
|
end
|
|
262
|
-
end
|
|
263
|
+
end
|
|
@@ -5,11 +5,49 @@ module Merb::RenderMixin
|
|
|
5
5
|
# ==== Parameters
|
|
6
6
|
# base<Module>:: Module that is including RenderMixin (probably a controller)
|
|
7
7
|
def self.included(base)
|
|
8
|
+
base.extend(ClassMethods)
|
|
8
9
|
base.class_eval do
|
|
9
|
-
class_inheritable_accessor :
|
|
10
|
+
class_inheritable_accessor :_default_render_options
|
|
10
11
|
end
|
|
11
12
|
end
|
|
12
13
|
|
|
14
|
+
module ClassMethods
|
|
15
|
+
|
|
16
|
+
# Return the default render options.
|
|
17
|
+
#
|
|
18
|
+
# ==== Returns
|
|
19
|
+
# Hash:: An options hash
|
|
20
|
+
def default_render_options
|
|
21
|
+
self._default_render_options ||= {}
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Set default render options at the class level.
|
|
25
|
+
#
|
|
26
|
+
# ==== Parameters
|
|
27
|
+
# opts<Hash>:: An options hash
|
|
28
|
+
def render_options(opts)
|
|
29
|
+
self._default_render_options = opts
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Set the default layout to use or nil/false to disable layout rendering.
|
|
33
|
+
# This is a shortcut for render_options :layout => false.
|
|
34
|
+
#
|
|
35
|
+
# ==== Parameters
|
|
36
|
+
# layout<~to_s>:: The layout that should be used for this class
|
|
37
|
+
#
|
|
38
|
+
# ==== Returns
|
|
39
|
+
# Hash:: The default render options.
|
|
40
|
+
def layout(layout)
|
|
41
|
+
self.default_render_options.update(:layout => (layout ? layout : false))
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Enable the default layout logic - reset the layout option.
|
|
45
|
+
def default_layout
|
|
46
|
+
self.default_render_options.delete(:layout)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
end
|
|
50
|
+
|
|
13
51
|
# Render the specified item, with the specified options.
|
|
14
52
|
#
|
|
15
53
|
# ==== Parameters
|
|
@@ -46,6 +84,9 @@ module Merb::RenderMixin
|
|
|
46
84
|
# render :format => :xml means render nil, :format => :xml
|
|
47
85
|
opts, thing = thing, nil if thing.is_a?(Hash)
|
|
48
86
|
|
|
87
|
+
# Merge with class level default render options
|
|
88
|
+
opts = self.class.default_render_options.merge(opts)
|
|
89
|
+
|
|
49
90
|
# If you don't specify a thing to render, assume they want to render the current action
|
|
50
91
|
thing ||= action_name.to_sym
|
|
51
92
|
|
|
@@ -91,7 +132,9 @@ module Merb::RenderMixin
|
|
|
91
132
|
# thing<String, Symbol>::
|
|
92
133
|
# The thing to attempt to render via #render before calling the transform
|
|
93
134
|
# method on the object. Defaults to nil.
|
|
94
|
-
# opts<Hash>::
|
|
135
|
+
# opts<Hash>::
|
|
136
|
+
# An options hash that will be used for rendering
|
|
137
|
+
# (passed on to #render or serialization methods like #to_json or #to_xml)
|
|
95
138
|
#
|
|
96
139
|
# ==== Returns
|
|
97
140
|
# String::
|
|
@@ -111,32 +154,59 @@ module Merb::RenderMixin
|
|
|
111
154
|
# display @object, :layout => "zoo"
|
|
112
155
|
# #=> display @object, nil, :layout => "zoo"
|
|
113
156
|
#
|
|
114
|
-
#
|
|
157
|
+
# If you need to pass extra parameters to serialization method, for instance,
|
|
158
|
+
# to exclude some of attributes or serialize associations, just pass options
|
|
159
|
+
# for it.
|
|
160
|
+
# For instance,
|
|
161
|
+
#
|
|
162
|
+
# display @locations, :except => [:locatable_type, :locatable_id], :include => [:locatable]
|
|
163
|
+
#
|
|
164
|
+
# serializes object with polymorphic association, not raw locatable_* attributes.
|
|
165
|
+
#
|
|
166
|
+
#
|
|
167
|
+
# ==== Options
|
|
168
|
+
#
|
|
169
|
+
# :template a template to use for rendering
|
|
170
|
+
# :layout a layout to use for rendering
|
|
171
|
+
|
|
172
|
+
# all other options options that will be pass to serialization method
|
|
173
|
+
# like #to_json or #to_xml
|
|
174
|
+
#
|
|
175
|
+
# ==== Notes
|
|
115
176
|
# The transformed object will not be used in a layout unless a :layout is
|
|
116
177
|
# explicitly passed in the opts.
|
|
178
|
+
#
|
|
117
179
|
def display(object, thing = nil, opts = {})
|
|
118
180
|
# display @object, "path/to/foo" means display @object, nil, :template => "path/to/foo"
|
|
119
181
|
# display @object, :template => "path/to/foo" means display @object, nil, :template => "path/to/foo"
|
|
120
|
-
|
|
182
|
+
template_opt = opts.delete(:template)
|
|
183
|
+
|
|
184
|
+
case thing
|
|
185
|
+
when String
|
|
186
|
+
template_opt, thing = thing, nil
|
|
187
|
+
when Hash
|
|
188
|
+
opts, thing = thing, nil
|
|
189
|
+
end
|
|
121
190
|
|
|
122
191
|
# Try to render without the object
|
|
123
|
-
render(thing || action_name.to_sym, opts)
|
|
192
|
+
render(thing || action_name.to_sym, opts.merge(:template => template_opt))
|
|
124
193
|
|
|
125
194
|
# If the render fails (i.e. a template was not found)
|
|
126
195
|
rescue TemplateNotFound
|
|
196
|
+
# Merge with class level default render options
|
|
197
|
+
opts = self.class.default_render_options.merge(opts)
|
|
127
198
|
|
|
128
199
|
# Figure out what to transform and raise NotAcceptable unless there's a transform method assigned
|
|
129
200
|
transform = Merb.mime_transform_method(content_type)
|
|
130
201
|
raise NotAcceptable unless transform && object.respond_to?(transform)
|
|
131
|
-
|
|
132
|
-
# Throw the transformed object for later consumption by the layout
|
|
133
|
-
throw_content(:for_layout, object.send(transform))
|
|
134
|
-
|
|
202
|
+
|
|
135
203
|
# Only use a layout if one was specified
|
|
136
|
-
|
|
204
|
+
layout_opt = opts.delete(:layout)
|
|
205
|
+
|
|
206
|
+
if layout_opt
|
|
137
207
|
# Look for the layout under the default layout directly. If it's not found, reraise
|
|
138
208
|
# the TemplateNotFound error
|
|
139
|
-
template = _template_location(
|
|
209
|
+
template = _template_location(layout_opt, layout.index(".") ? content_type : nil, "layout")
|
|
140
210
|
layout = _template_for(_template_root / template) ||
|
|
141
211
|
(raise TemplateNotFound, "No layout found at #{_template_root / template}.*")
|
|
142
212
|
|
|
@@ -145,6 +215,12 @@ module Merb::RenderMixin
|
|
|
145
215
|
|
|
146
216
|
# Otherwise, just render the transformed object
|
|
147
217
|
else
|
|
218
|
+
unless opts.empty?
|
|
219
|
+
# there are options for serialization method
|
|
220
|
+
throw_content(:for_layout, object.send(transform, opts))
|
|
221
|
+
else
|
|
222
|
+
throw_content(:for_layout, object.send(transform))
|
|
223
|
+
end
|
|
148
224
|
catch_content(:for_layout)
|
|
149
225
|
end
|
|
150
226
|
end
|
|
@@ -195,7 +271,11 @@ module Merb::RenderMixin
|
|
|
195
271
|
end.join
|
|
196
272
|
else
|
|
197
273
|
@_merb_partial_locals = opts
|
|
198
|
-
|
|
274
|
+
if template_method && self.respond_to?(template_method)
|
|
275
|
+
sent_template = send(template_method)
|
|
276
|
+
else
|
|
277
|
+
raise TemplateNotFound, "Could not find template at #{template_location}.*"
|
|
278
|
+
end
|
|
199
279
|
end
|
|
200
280
|
@_merb_partial_locals = @_old_partial_locals.pop
|
|
201
281
|
sent_template
|
|
@@ -235,9 +315,7 @@ module Merb::RenderMixin
|
|
|
235
315
|
# one in to this method), and not found. No error will be raised if no
|
|
236
316
|
# layout was specified, and the default layouts were not found.
|
|
237
317
|
def _get_layout(layout = nil)
|
|
238
|
-
|
|
239
|
-
layout = _layout.instance_of?(Symbol) && self.respond_to?(_layout, true) ? send(_layout) : _layout
|
|
240
|
-
end
|
|
318
|
+
layout = layout.instance_of?(Symbol) && self.respond_to?(layout, true) ? send(layout) : layout
|
|
241
319
|
layout = layout.to_s if layout
|
|
242
320
|
|
|
243
321
|
# If a layout was provided, throw an error if it's not found
|
|
@@ -258,24 +336,30 @@ module Merb::RenderMixin
|
|
|
258
336
|
# and template location of the first match.
|
|
259
337
|
#
|
|
260
338
|
# ==== Parameters
|
|
261
|
-
#
|
|
339
|
+
# context<Object>:: The controller action or template basename.
|
|
262
340
|
# content_type<~to_s>:: The content type. Defaults to nil.
|
|
263
341
|
# controller<~to_s>:: The name of the controller. Defaults to nil.
|
|
264
342
|
#
|
|
265
343
|
# ==== Options (opts)
|
|
266
344
|
# :template<String>::
|
|
267
345
|
# The location of the template to use. Defaults to whatever matches this
|
|
268
|
-
#
|
|
346
|
+
# context, content_type and controller.
|
|
269
347
|
#
|
|
270
348
|
# ==== Returns
|
|
271
349
|
# Array[Symbol, String]::
|
|
272
350
|
# A pair consisting of the template method and location.
|
|
273
|
-
def _template_for(
|
|
351
|
+
def _template_for(context, content_type, controller=nil, opts={})
|
|
274
352
|
template_method = nil
|
|
275
353
|
template_location = nil
|
|
276
354
|
|
|
277
355
|
self.class._template_roots.reverse_each do |root, template_location|
|
|
278
|
-
|
|
356
|
+
if opts[:template] # use the given template as the location context
|
|
357
|
+
template_location = root / self.send(template_location, opts[:template], content_type, nil)
|
|
358
|
+
template_method = Merb::Template.template_for(template_location)
|
|
359
|
+
break if template_method && self.respond_to?(template_method)
|
|
360
|
+
end
|
|
361
|
+
|
|
362
|
+
template_location = root / (opts[:template] || self.send(template_location, context, content_type, controller))
|
|
279
363
|
template_method = Merb::Template.template_for(template_location)
|
|
280
364
|
break if template_method && self.respond_to?(template_method)
|
|
281
365
|
end
|
|
@@ -296,6 +380,16 @@ module Merb::RenderMixin
|
|
|
296
380
|
@_caught_content[obj]
|
|
297
381
|
end
|
|
298
382
|
|
|
383
|
+
# Called in templates to test for the existence of previously thrown content.
|
|
384
|
+
#
|
|
385
|
+
# ==== Parameters
|
|
386
|
+
# obj<Object>:: The key in the thrown_content hash. Defaults to :for_layout.
|
|
387
|
+
#---
|
|
388
|
+
# @public
|
|
389
|
+
def thrown_content?(obj = :for_layout)
|
|
390
|
+
@_caught_content.key?(obj)
|
|
391
|
+
end
|
|
392
|
+
|
|
299
393
|
# Called in templates to store up content for later use. Takes a string
|
|
300
394
|
# and/or a block. First, the string is evaluated, and then the block is
|
|
301
395
|
# captured using the capture() helper provided by the template languages. The
|