merb 0.4.1 → 0.4.2
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 +138 -56
- data/Rakefile +23 -8
- data/app_generators/merb/templates/Rakefile +13 -0
- data/app_generators/merb/templates/app/helpers/global_helper.rb +1 -1
- data/app_generators/merb/templates/app/views/exceptions/internal_server_error.html.erb +12 -3
- data/app_generators/merb/templates/config/merb.yml +14 -1
- data/app_generators/merb/templates/spec/spec_helper.rb +6 -0
- data/app_generators/merb/templates/test/test_helper.rb +1 -0
- data/lib/merb.rb +27 -7
- data/lib/merb/abstract_controller.rb +76 -36
- data/lib/merb/caching/store/memcache.rb +20 -0
- data/lib/merb/constants.rb +2 -4
- data/lib/merb/controller.rb +44 -2
- data/lib/merb/core_ext/get_args.rb +23 -4
- data/lib/merb/core_ext/hash.rb +16 -11
- data/lib/merb/core_ext/inflections.rb +1 -1
- data/lib/merb/core_ext/kernel.rb +106 -26
- data/lib/merb/core_ext/numeric.rb +1 -1
- data/lib/merb/core_ext/string.rb +10 -13
- data/lib/merb/dispatcher.rb +2 -2
- data/lib/merb/exceptions.rb +3 -1
- data/lib/merb/logger.rb +15 -6
- data/lib/merb/mail_controller.rb +18 -2
- data/lib/merb/mailer.rb +1 -1
- data/lib/merb/mixins/controller.rb +64 -228
- data/lib/merb/mixins/erubis_capture.rb +1 -1
- data/lib/merb/mixins/general_controller.rb +258 -0
- data/lib/merb/mixins/render.rb +45 -24
- data/lib/merb/mixins/responder.rb +89 -18
- data/lib/merb/mixins/view_context.rb +32 -5
- data/lib/merb/mixins/web_controller.rb +8 -1
- data/lib/merb/mongrel_handler.rb +27 -17
- data/lib/merb/part_controller.rb +10 -0
- data/lib/merb/request.rb +34 -14
- data/lib/merb/router.rb +77 -45
- data/lib/merb/server.rb +116 -72
- data/lib/merb/session/cookie_store.rb +14 -22
- data/lib/merb/session/mem_cache_session.rb +2 -2
- data/lib/merb/session/memory_session.rb +12 -1
- data/lib/merb/template/erubis.rb +31 -0
- data/lib/merb/template/haml.rb +4 -14
- data/lib/merb/template/xml_builder.rb +1 -1
- data/lib/merb/test/helper.rb +90 -18
- data/lib/merb/test/rspec.rb +145 -74
- data/lib/merb/version.rb +11 -0
- data/lib/merb/view_context.rb +3 -6
- data/lib/patch +69 -0
- data/lib/tasks/merb.rake +1 -1
- data/spec/fixtures/config/environments/environment_config_test.yml +1 -0
- data/spec/fixtures/controllers/render_spec_controllers.rb +63 -4
- data/spec/fixtures/views/examples/template_throw_content_without_block.html.erb +3 -0
- data/spec/fixtures/views/partials/_erubis.html.erb +1 -1
- data/spec/merb/abstract_controller_spec.rb +1 -0
- data/spec/merb/controller_filters_spec.rb +68 -3
- data/spec/merb/controller_spec.rb +35 -68
- data/spec/merb/cookie_store_spec.rb +7 -20
- data/spec/merb/core_ext_spec.rb +35 -1
- data/spec/merb/dispatch_spec.rb +8 -2
- data/spec/merb/generator_spec.rb +12 -4
- data/spec/merb/mail_controller_spec.rb +33 -0
- data/spec/merb/part_controller_spec.rb +33 -1
- data/spec/merb/render_spec.rb +74 -0
- data/spec/merb/request_spec.rb +43 -0
- data/spec/merb/responder_spec.rb +1 -0
- data/spec/merb/router_spec.rb +118 -13
- data/spec/merb/server_spec.rb +19 -0
- data/spec/merb/view_context_spec.rb +31 -3
- data/spec/spec_helper.rb +8 -0
- data/spec/spec_helpers/url_shared_behaviour.rb +112 -0
- metadata +124 -87
@@ -6,7 +6,7 @@ module Merb
|
|
6
6
|
|
7
7
|
# Provides direct acccess to the buffer for this view context
|
8
8
|
def _buffer( the_binding )
|
9
|
-
@_buffer
|
9
|
+
@_buffer = eval( "_buf", the_binding )
|
10
10
|
end
|
11
11
|
|
12
12
|
# Capture allows you to extract a part of the template into an
|
@@ -0,0 +1,258 @@
|
|
1
|
+
# This module provides helper style functionality to all controllers.
|
2
|
+
module Merb
|
3
|
+
module GeneralControllerMixin
|
4
|
+
include Merb::ControllerExceptions
|
5
|
+
|
6
|
+
# Returns a URL according to the defined route. Accepts the path and
|
7
|
+
# an options hash. The path specifies the route requested. The options
|
8
|
+
# hash fills in the dynamic parts of the route.
|
9
|
+
#
|
10
|
+
# Merb routes can often be one-way; if they use a regex to define
|
11
|
+
# the route, then knowing the controller & action won't be enough
|
12
|
+
# to reverse-generate the route. However, if you use the default
|
13
|
+
# /controller/action/id?query route, +default_route+ can generate
|
14
|
+
# it for you.
|
15
|
+
#
|
16
|
+
# For easy reverse-routes that use a Regex, be sure to also add
|
17
|
+
# a name to the route, so +url+ can find it.
|
18
|
+
#
|
19
|
+
# Nested resources such as:
|
20
|
+
#
|
21
|
+
# r.resources :blogposts do |post|
|
22
|
+
# post.resources :comments
|
23
|
+
# end
|
24
|
+
#
|
25
|
+
# Provide the following routes:
|
26
|
+
#
|
27
|
+
# [:blogposts, "/blogposts"]
|
28
|
+
# [:blogpost, "/blogposts/:id"]
|
29
|
+
# [:edit_blogpost, "/blogposts/:id/edit"]
|
30
|
+
# [:new_blogpost, "/blogposts/new"]
|
31
|
+
# [:custom_new_blogpost, "/blogposts/new/:action"]
|
32
|
+
# [:comments, "/blogposts/:blogpost_id/comments"]
|
33
|
+
# [:comment, "/blogposts/:blogpost_id/comments/:id"]
|
34
|
+
# [:edit_comment, "/blogposts/:blogpost_id/comments/:id/edit"]
|
35
|
+
# [:new_comment, "/blogposts/:blogpost_id/comments/new"]
|
36
|
+
# [:custom_new_comment, "/blogposts/:blogpost_id/comments/new/:action"]
|
37
|
+
#
|
38
|
+
#
|
39
|
+
# ==== Parameters
|
40
|
+
#
|
41
|
+
# :route_name: - Symbol that represents a named route that you want to use, such as +:edit_post+.
|
42
|
+
# :new_params: - Parameters to be passed to the generated URL, such as the +id+ for a record to edit.
|
43
|
+
#
|
44
|
+
# ==== Examples
|
45
|
+
#
|
46
|
+
# @post = Post.find(1)
|
47
|
+
# @comment = @post.comments.find(1)
|
48
|
+
#
|
49
|
+
# url(:blogposts) # => /blogposts
|
50
|
+
# url(:new_post) # => /blogposts/new
|
51
|
+
# url(:blogpost, @post) # => /blogposts/1
|
52
|
+
# url(:edit_blogpost, @post) # => /blogposts/1/edit
|
53
|
+
# url(:custom_new_blogpost, :action => 'alternate') # => /blogposts/new/alternate
|
54
|
+
#
|
55
|
+
# url(:comments, :blogpost => @post) # => /blogposts/1/comments
|
56
|
+
# url(:new_comment, :blogpost => @post) # => /blogposts/1/comments/new
|
57
|
+
# url(:comment, @comment) # => /blogposts/1/comments/1
|
58
|
+
# url(:edit_comment, @comment) # => /blogposts/1/comments/1/edit
|
59
|
+
# url(:custom_new_comment, :blogpost => @post)
|
60
|
+
#
|
61
|
+
# url(:page => 2) # => /posts/show/1?page=2
|
62
|
+
# url(:new_post, :page => 3) # => /posts/new?page=3
|
63
|
+
# url('/go/here', :page => 3) # => /go/here?page=3
|
64
|
+
#
|
65
|
+
# url(:controller => "welcome") # => /welcome
|
66
|
+
# url(:controller => "welcome", :action => "greet")
|
67
|
+
# # => /welcome/greet
|
68
|
+
#
|
69
|
+
def url(route_name = nil, new_params = {})
|
70
|
+
if route_name.is_a?(Hash)
|
71
|
+
new_params = route_name
|
72
|
+
route_name = nil
|
73
|
+
end
|
74
|
+
|
75
|
+
url = if new_params.respond_to?(:keys) && route_name.nil? &&
|
76
|
+
!(new_params.keys & [:controller, :action, :id]).empty?
|
77
|
+
url_from_default_route(new_params)
|
78
|
+
elsif route_name.nil? && !route.regexp?
|
79
|
+
url_from_route(route, new_params)
|
80
|
+
elsif route_name.nil?
|
81
|
+
request.path + (new_params.empty? ? "" : "?" + params_to_query_string(new_params))
|
82
|
+
elsif route_name.is_a?(Symbol)
|
83
|
+
url_from_route(route_name, new_params)
|
84
|
+
elsif route_name.is_a?(String)
|
85
|
+
route_name + (new_params.empty? ? "" : "?" + params_to_query_string(new_params))
|
86
|
+
else
|
87
|
+
raise "URL not generated: #{route_name.inspect}, #{new_params.inspect}"
|
88
|
+
end
|
89
|
+
url = Merb::Server.config[:path_prefix] + url if Merb::Server.config[:path_prefix]
|
90
|
+
url
|
91
|
+
end
|
92
|
+
|
93
|
+
def url_from_route(symbol, new_params = {})
|
94
|
+
if new_params.respond_to?(:new_record?) && new_params.new_record?
|
95
|
+
symbol = "#{symbol}".singularize.to_sym
|
96
|
+
new_params = {}
|
97
|
+
end
|
98
|
+
route = symbol.is_a?(Symbol) ? Merb::Router.named_routes[symbol] : symbol
|
99
|
+
unless route
|
100
|
+
raise InternalServerError, "URL could not be constructed. Route symbol not found: #{symbol.inspect}"
|
101
|
+
end
|
102
|
+
path = route.generate(new_params, params)
|
103
|
+
keys = route.symbol_segments
|
104
|
+
if new_params.is_a? Hash
|
105
|
+
if ext = format_extension(new_params)
|
106
|
+
new_params.delete(:format)
|
107
|
+
path += "." + ext
|
108
|
+
end
|
109
|
+
extras = new_params.reject{ |k, v| keys.include?(k) }
|
110
|
+
path += "?" + params_to_query_string(extras) unless extras.empty?
|
111
|
+
end
|
112
|
+
path
|
113
|
+
end
|
114
|
+
|
115
|
+
# this is pretty ugly, but it works. TODO: make this cleaner
|
116
|
+
def url_from_default_route(new_params)
|
117
|
+
query_params = new_params.reject do |k,v|
|
118
|
+
[:controller, :action, :id, :format].include?(k)
|
119
|
+
end
|
120
|
+
controller = get_controller_for_url_generation(new_params)
|
121
|
+
url = "/#{controller}"
|
122
|
+
if new_params[:action] || new_params[:id] ||
|
123
|
+
new_params[:format] || !query_params.empty?
|
124
|
+
action = new_params[:action] || params[:action]
|
125
|
+
url += "/#{action}"
|
126
|
+
end
|
127
|
+
if new_params[:id]
|
128
|
+
url += "/#{new_params[:id]}"
|
129
|
+
end
|
130
|
+
if format = new_params[:format]
|
131
|
+
format = params[:format] if format == :current
|
132
|
+
url += ".#{format}"
|
133
|
+
end
|
134
|
+
unless query_params.empty?
|
135
|
+
url += "?" + params_to_query_string(query_params)
|
136
|
+
end
|
137
|
+
url
|
138
|
+
end
|
139
|
+
|
140
|
+
protected
|
141
|
+
|
142
|
+
# Creates query string from params, supporting nested arrays and hashes.
|
143
|
+
# ==== Example
|
144
|
+
# params_to_query_string(:user => {:filter => {:name => "quux*"}, :order => ["name"]})
|
145
|
+
# # => user[filter][name]=quux%2A&user[order][]=name
|
146
|
+
def params_to_query_string(value, prefix = nil)
|
147
|
+
case value
|
148
|
+
when Array
|
149
|
+
value.map { |v|
|
150
|
+
params_to_query_string(v, "#{prefix}[]")
|
151
|
+
} * "&"
|
152
|
+
when Hash
|
153
|
+
value.map { |k, v|
|
154
|
+
params_to_query_string(v, prefix ? "#{prefix}[#{escape(k)}]" : escape(k))
|
155
|
+
} * "&"
|
156
|
+
else
|
157
|
+
"#{prefix}=#{escape(value)}"
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
# +format_extension+ dictates when named route URLs generated by the url
|
162
|
+
# method will have a file extension. It will return either false or the format
|
163
|
+
# extension to append.
|
164
|
+
#
|
165
|
+
# ==== Configuration Options
|
166
|
+
#
|
167
|
+
# By default, non-HTML URLs will be given an extension. It is posible
|
168
|
+
# to override this behaviour by setting +:use_format_in_urls+ in your
|
169
|
+
# Merb config (merb.yml) to either true/false.
|
170
|
+
#
|
171
|
+
# +true+ Results in all URLs (even HTML) being given extensions.
|
172
|
+
# This effect is often desirable when you have many formats and dont
|
173
|
+
# wish to treat .html any differently than any other format.
|
174
|
+
# +false+ Results in no URLs being given extensions and +format+
|
175
|
+
# gets treated just like any other param (default).
|
176
|
+
#
|
177
|
+
# ==== Method parameters
|
178
|
+
#
|
179
|
+
# +new_params+ - New parameters to be appended to the URL
|
180
|
+
#
|
181
|
+
# ==== Examples
|
182
|
+
#
|
183
|
+
# url(:post, :id => post, :format => 'xml')
|
184
|
+
# # => /posts/34.xml
|
185
|
+
#
|
186
|
+
# url(:accounts, :format => 'yml')
|
187
|
+
# # => /accounts.yml
|
188
|
+
#
|
189
|
+
# url(:edit_product, :id => 3, :format => 'html')
|
190
|
+
# # => /products/3
|
191
|
+
#
|
192
|
+
def format_extension(new_params={})
|
193
|
+
use_format = Merb::Server.config[:use_format_in_urls]
|
194
|
+
if use_format.nil?
|
195
|
+
prms = params.merge(new_params)
|
196
|
+
use_format = prms[:format] != 'html' && prms[:format]
|
197
|
+
end
|
198
|
+
use_format
|
199
|
+
end
|
200
|
+
|
201
|
+
|
202
|
+
# Creates an MD5 hashed token based on the current time.
|
203
|
+
#
|
204
|
+
# ==== Example
|
205
|
+
# make_token
|
206
|
+
# # => "b9a82e011694cc13a4249731b9e83cea"
|
207
|
+
#
|
208
|
+
def make_token
|
209
|
+
require 'digest/md5'
|
210
|
+
Digest::MD5.hexdigest("#{inspect}#{Time.now}#{rand}")
|
211
|
+
end
|
212
|
+
|
213
|
+
# Escapes the string representation of +obj+ and escapes
|
214
|
+
# it for use in XML.
|
215
|
+
#
|
216
|
+
# ==== Parameter
|
217
|
+
#
|
218
|
+
# +obj+ - The object to escape for use in XML.
|
219
|
+
#
|
220
|
+
def escape_xml(obj)
|
221
|
+
obj.to_s.gsub(/[&<>"']/) { |s| Merb::Const::ESCAPE_TABLE[s] }
|
222
|
+
end
|
223
|
+
alias h escape_xml
|
224
|
+
alias html_escape escape_xml
|
225
|
+
|
226
|
+
# Escapes +s+ for use in a URL.
|
227
|
+
#
|
228
|
+
# ==== Parameter
|
229
|
+
#
|
230
|
+
# +s+ - String to URL escape.
|
231
|
+
#
|
232
|
+
def escape(s)
|
233
|
+
s.to_s.gsub(/([^ a-zA-Z0-9_.-]+)/n) {
|
234
|
+
'%'+$1.unpack('H2'*$1.size).join('%').upcase
|
235
|
+
}.tr(' ', '+')
|
236
|
+
end
|
237
|
+
|
238
|
+
# Unescapes a string (i.e., reverse URL escaping).
|
239
|
+
#
|
240
|
+
# ==== Parameter
|
241
|
+
#
|
242
|
+
# +s+ - String to unescape.
|
243
|
+
#
|
244
|
+
def unescape(s)
|
245
|
+
s.tr('+', ' ').gsub(/((?:%[0-9a-fA-F]{2})+)/n){
|
246
|
+
[$1.delete('%')].pack('H*')
|
247
|
+
}
|
248
|
+
end
|
249
|
+
|
250
|
+
private
|
251
|
+
# Used for sepccing
|
252
|
+
def get_controller_for_url_generation(options)
|
253
|
+
raise "Controller Not Specified" unless options[:controller]
|
254
|
+
options[:controller]
|
255
|
+
end
|
256
|
+
|
257
|
+
end
|
258
|
+
end
|
data/lib/merb/mixins/render.rb
CHANGED
@@ -5,7 +5,7 @@ module Merb
|
|
5
5
|
include Merb::ControllerExceptions
|
6
6
|
|
7
7
|
def self.included(base)
|
8
|
-
base.class_eval {
|
8
|
+
base.class_eval {
|
9
9
|
class_inheritable_accessor :_template_root,
|
10
10
|
:_layout,
|
11
11
|
:_templates,
|
@@ -16,7 +16,7 @@ module Merb
|
|
16
16
|
self._templates = {}
|
17
17
|
self._cached_partials = {}
|
18
18
|
|
19
|
-
attr_accessor :template
|
19
|
+
attr_accessor :template
|
20
20
|
}
|
21
21
|
end
|
22
22
|
|
@@ -131,7 +131,7 @@ module Merb
|
|
131
131
|
# This will ensure that all instance variable are up to date in your views.
|
132
132
|
#
|
133
133
|
def render(*args,&blk)
|
134
|
-
opts = Hash === args.last ? args.pop : {}
|
134
|
+
opts = (Hash === args.last) ? args.pop : {}
|
135
135
|
|
136
136
|
action = opts[:action] || params[:action]
|
137
137
|
opts[:layout] ||= _layout
|
@@ -145,7 +145,19 @@ module Merb
|
|
145
145
|
fmt = content_type
|
146
146
|
if transform_method = Merb.mime_transform_method(fmt)
|
147
147
|
set_response_headers fmt
|
148
|
-
|
148
|
+
transform_args = provided_format_arguments_for(fmt)
|
149
|
+
return case transform_args
|
150
|
+
when Hash then obj.send(transform_method, transform_args)
|
151
|
+
when Array then obj.send(transform_method, *transform_args)
|
152
|
+
when Proc then
|
153
|
+
case transform_args.arity
|
154
|
+
when 3 then transform_args.call(obj, self, transform_method)
|
155
|
+
when 2 then transform_args.call(obj, self)
|
156
|
+
when 1 then transform_args.call(obj)
|
157
|
+
else transform_args.call
|
158
|
+
end
|
159
|
+
else obj.send(transform_method)
|
160
|
+
end
|
149
161
|
end
|
150
162
|
end
|
151
163
|
end
|
@@ -255,9 +267,13 @@ module Merb
|
|
255
267
|
return " "
|
256
268
|
end
|
257
269
|
|
270
|
+
# Sets the response's status to the specified value. Use either an
|
271
|
+
# integer (200, 201, 302, etc.), or a Symbol as defined in
|
272
|
+
# Merb::ControllerExceptions::RESPONSE_CODES, such as :not_found,
|
273
|
+
# :created or :see_other.
|
258
274
|
def set_status(status)
|
259
275
|
if status.kind_of?(Symbol)
|
260
|
-
status = Merb::ControllerExceptions::
|
276
|
+
status = Merb::ControllerExceptions::STATUS_CODES[status]
|
261
277
|
status || raise("Can't find a response code with that name")
|
262
278
|
end
|
263
279
|
@_status = status
|
@@ -286,7 +302,7 @@ module Merb
|
|
286
302
|
opts[:as] ||= template[(template.rindex('/_') + 2)..-1].split('.').first
|
287
303
|
|
288
304
|
if opts[:with] # Render a collection or an object
|
289
|
-
partial_for_collection(template, opts
|
305
|
+
partial_for_collection(template, opts.delete(:with), opts)
|
290
306
|
else # Just render a partial
|
291
307
|
engine = Template.engine_for(template)
|
292
308
|
render_partial(template, engine, opts || {})
|
@@ -303,29 +319,34 @@ module Merb
|
|
303
319
|
private
|
304
320
|
|
305
321
|
def render_partial(template, engine, locals={})
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
322
|
+
@_merb_partial_locals = locals
|
323
|
+
options = {
|
324
|
+
:file => template,
|
325
|
+
:view_context => clean_view_context(engine),
|
326
|
+
:opts => { :locals => locals }
|
327
|
+
}
|
328
|
+
engine.transform(options)
|
313
329
|
end
|
314
330
|
|
315
331
|
def partial_for_collection(template, collection, opts={})
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
opts.delete(:with)
|
332
|
+
# Delete the internal keys, so that everything else is considered
|
333
|
+
# a local declaration in the partial
|
334
|
+
local_name = opts.delete(:as)
|
320
335
|
|
321
|
-
|
336
|
+
engine = Template.engine_for(template)
|
337
|
+
|
338
|
+
buffer = []
|
339
|
+
|
340
|
+
collection = [collection].flatten
|
341
|
+
collection.each_with_index do |object, count|
|
342
|
+
opts.merge!({
|
343
|
+
local_name.to_sym => object,
|
344
|
+
:count => count
|
345
|
+
})
|
346
|
+
buffer << render_partial(template, engine, opts)
|
347
|
+
end
|
322
348
|
|
323
|
-
|
324
|
-
[ collection ].flatten.each do |object|
|
325
|
-
opts.merge!({local_name.to_sym => object})
|
326
|
-
ret << render_partial(template, engine, opts)
|
327
|
-
end
|
328
|
-
ret
|
349
|
+
buffer.join
|
329
350
|
end
|
330
351
|
|
331
352
|
# this returns a ViewContext object populated with all
|
@@ -18,17 +18,29 @@ module Merb
|
|
18
18
|
raise ArgumentError unless key.is_a?(Symbol) && values.is_a?(Array)
|
19
19
|
ResponderMixin::Rest::TYPES.update(key => values)
|
20
20
|
add_response_headers!(key, new_response_headers)
|
21
|
-
ResponderMixin::Rest::TRANSFORM_METHODS.merge!(key => transform_method)
|
21
|
+
ResponderMixin::Rest::TRANSFORM_METHODS.merge!(key => transform_method)
|
22
22
|
end
|
23
23
|
|
24
24
|
def remove_mime_type(key)
|
25
25
|
key == :all ? false : ResponderMixin::Rest::TYPES.delete(key)
|
26
26
|
end
|
27
27
|
|
28
|
+
# Return the method name (if any) for the mimetype
|
28
29
|
def mime_transform_method(key)
|
29
30
|
ResponderMixin::Rest::TRANSFORM_METHODS[key]
|
30
31
|
end
|
31
32
|
|
33
|
+
# Return default arguments for transform method (if any)
|
34
|
+
def mime_transform_method_defaults(key)
|
35
|
+
ResponderMixin::Rest::TRANSFORM_METHOD_DEFAULTS[key]
|
36
|
+
end
|
37
|
+
|
38
|
+
# Set default arguments/proc for a format transform method
|
39
|
+
def set_mime_transform_method_defaults(key, *args, &block)
|
40
|
+
raise "Unknown mimetype #{key}" unless ResponderMixin::Rest::TRANSFORM_METHODS[key]
|
41
|
+
args = block if block_given?
|
42
|
+
ResponderMixin::Rest::TRANSFORM_METHOD_DEFAULTS[key] = args unless args.empty?
|
43
|
+
end
|
32
44
|
|
33
45
|
# Adds outgoing headers to a mime type. This can be done with the Merb.add_mime_type method
|
34
46
|
# or directly here.
|
@@ -165,8 +177,11 @@ module Merb
|
|
165
177
|
|
166
178
|
def self.included(base) # :nodoc:
|
167
179
|
base.extend(ClassMethods)
|
168
|
-
base.class_eval
|
169
|
-
|
180
|
+
base.class_eval do
|
181
|
+
class_inheritable_accessor :class_provided_formats
|
182
|
+
class_inheritable_accessor :class_provided_format_arguments
|
183
|
+
end
|
184
|
+
base.reset_provides
|
170
185
|
end
|
171
186
|
|
172
187
|
module ClassMethods
|
@@ -174,56 +189,103 @@ module Merb
|
|
174
189
|
# Adds symbols representing formats to the controller's
|
175
190
|
# default list of provided_formats. These will apply to
|
176
191
|
# every action in the controller, unless modified in the action.
|
177
|
-
|
178
|
-
|
192
|
+
# If the last argument is a Hash or an Array, these are regarded
|
193
|
+
# as arguments to pass to the to_<mime_type> method as needed.
|
194
|
+
def provides(*formats, &block)
|
195
|
+
options = extract_provides_options(formats, &block)
|
196
|
+
formats.each do |fmt|
|
179
197
|
self.class_provided_formats << fmt unless class_provided_formats.include?(fmt)
|
180
|
-
|
198
|
+
self.class_provided_format_arguments[fmt] = options unless options.nil?
|
199
|
+
end
|
181
200
|
end
|
182
201
|
|
183
202
|
# Overwrites the controller's list of provided_formats. These
|
184
203
|
# will apply to every action in the controller, unless modified
|
185
204
|
# in the action.
|
186
205
|
def only_provides(*formats)
|
187
|
-
|
206
|
+
clear_provides
|
207
|
+
provides(*formats)
|
188
208
|
end
|
189
209
|
|
190
210
|
# Removes formats from the controller's
|
191
|
-
# default list of provided_formats.
|
211
|
+
# default list of provided_formats. These will apply to
|
192
212
|
# every action in the controller, unless modified in the action.
|
193
213
|
def does_not_provide(*formats)
|
194
214
|
self.class_provided_formats -= formats
|
215
|
+
formats.each { |fmt| self.class_provided_format_arguments.delete(fmt) }
|
195
216
|
end
|
217
|
+
|
218
|
+
# Clear any formats and their options
|
219
|
+
def clear_provides
|
220
|
+
self.class_provided_formats = []
|
221
|
+
self.class_provided_format_arguments = {}
|
222
|
+
end
|
223
|
+
|
224
|
+
# Reset to the default list of formats
|
225
|
+
def reset_provides
|
226
|
+
only_provides(:html)
|
227
|
+
end
|
228
|
+
|
229
|
+
# Extract arguments for provided format methods
|
230
|
+
def extract_provides_options(args, &block)
|
231
|
+
return block if block_given?
|
232
|
+
case args.last
|
233
|
+
when Hash then [args.pop]
|
234
|
+
when Array then args.pop
|
235
|
+
when Proc then args.pop
|
236
|
+
else nil
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
196
240
|
end
|
197
241
|
|
198
242
|
# Returns the current list of formats provided for this instance
|
199
243
|
# of the controller. It starts with what has been set in the controller
|
200
244
|
# (or :html by default) but can be modifed on a per-action basis.
|
201
245
|
def provided_formats
|
202
|
-
@_provided_formats ||= class_provided_formats
|
246
|
+
@_provided_formats ||= class_provided_formats.dup
|
203
247
|
end
|
204
248
|
|
205
249
|
# Sets the provided formats for this action. Usually, you would
|
206
250
|
# use a combination of +provides+, +only_provides+ and +does_not_provide+
|
207
251
|
# to manage this, but you can set it directly.
|
208
|
-
|
209
|
-
|
210
|
-
|
252
|
+
# If the last argument is a Hash or an Array, these are regarded
|
253
|
+
# as arguments to pass to the to_<mime_type> method as needed.
|
254
|
+
def set_provided_formats(*formats, &block)
|
255
|
+
raise_if_content_type_already_set!
|
256
|
+
@_provided_formats = []
|
257
|
+
@_provided_format_arguments = {}
|
258
|
+
provides(*formats.flatten, &block)
|
259
|
+
end
|
260
|
+
alias :provided_formats= :set_provided_formats
|
261
|
+
|
262
|
+
# Returns a Hash of arguments for format methods
|
263
|
+
def provided_format_arguments
|
264
|
+
@_provided_format_arguments ||= Hash.new.replace(class_provided_format_arguments)
|
265
|
+
end
|
266
|
+
|
267
|
+
# Returns the arguments (if any) for the mime_transform_method call
|
268
|
+
def provided_format_arguments_for(fmt)
|
269
|
+
self.provided_format_arguments[fmt] || Merb.mime_transform_method_defaults(fmt)
|
211
270
|
end
|
212
271
|
|
213
272
|
# Adds formats to the list of provided formats for this particular
|
214
273
|
# request. Usually used to add formats to a single action. See also
|
215
274
|
# the controller-level provides that affects all actions in a controller.
|
216
|
-
def provides(*formats)
|
217
|
-
|
218
|
-
|
219
|
-
|
275
|
+
def provides(*formats, &block)
|
276
|
+
raise_if_content_type_already_set!
|
277
|
+
options = self.class.extract_provides_options(formats, &block)
|
278
|
+
formats.each do |fmt|
|
279
|
+
self.provided_formats << fmt unless provided_formats.include?(fmt)
|
280
|
+
self.provided_format_arguments[fmt] = options unless options.nil?
|
281
|
+
end
|
220
282
|
end
|
221
283
|
|
222
284
|
# Sets list of provided formats for this particular
|
223
285
|
# request. Usually used to limit formats to a single action. See also
|
224
286
|
# the controller-level provides that affects all actions in a controller.
|
225
287
|
def only_provides(*formats)
|
226
|
-
self.
|
288
|
+
self.set_provided_formats(*formats)
|
227
289
|
end
|
228
290
|
|
229
291
|
# Removes formats from the list of provided formats for this particular
|
@@ -231,7 +293,9 @@ module Merb
|
|
231
293
|
# also the controller-level provides that affects all actions in a
|
232
294
|
# controller.
|
233
295
|
def does_not_provide(*formats)
|
234
|
-
|
296
|
+
formats.flatten!
|
297
|
+
self.provided_formats -= formats
|
298
|
+
formats.each { |fmt| self.provided_format_arguments.delete(fmt) }
|
235
299
|
end
|
236
300
|
|
237
301
|
# Do the content negotiation:
|
@@ -297,11 +361,18 @@ module Merb
|
|
297
361
|
@_content_type = new_type
|
298
362
|
end
|
299
363
|
|
364
|
+
private
|
365
|
+
|
366
|
+
def raise_if_content_type_already_set!
|
367
|
+
raise "Cannot modify provided_formats because content_type has already been set" if content_type_set?
|
368
|
+
end
|
369
|
+
|
300
370
|
module Rest
|
301
371
|
|
302
372
|
TYPES = {}
|
303
373
|
RESPONSE_HEADERS = {}
|
304
374
|
TRANSFORM_METHODS = {}
|
375
|
+
TRANSFORM_METHOD_DEFAULTS = {}
|
305
376
|
|
306
377
|
class Responder
|
307
378
|
|