merb-core 0.9.2 → 0.9.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|