merb 0.3.4 → 0.3.7
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 +206 -197
- data/Rakefile +12 -21
- data/bin/merb +1 -1
- data/examples/skeleton/Rakefile +6 -20
- data/examples/skeleton/dist/app/mailers/layout/application.erb +1 -0
- data/examples/skeleton/dist/conf/database.yml +23 -0
- data/examples/skeleton/dist/conf/environments/development.rb +1 -0
- data/examples/skeleton/dist/conf/environments/production.rb +1 -0
- data/examples/skeleton/dist/conf/environments/test.rb +1 -0
- data/examples/skeleton/dist/conf/merb.yml +32 -28
- data/examples/skeleton/dist/conf/merb_init.rb +16 -13
- data/examples/skeleton/dist/conf/router.rb +9 -9
- data/examples/skeleton/dist/schema/migrations/001_add_sessions_table.rb +2 -2
- data/lib/merb.rb +23 -18
- data/lib/merb/caching/fragment_cache.rb +3 -7
- data/lib/merb/caching/store/memcache.rb +20 -0
- data/lib/merb/core_ext/merb_array.rb +0 -0
- data/lib/merb/core_ext/merb_class.rb +44 -4
- data/lib/merb/core_ext/merb_enumerable.rb +43 -1
- data/lib/merb/core_ext/merb_hash.rb +200 -122
- data/lib/merb/core_ext/merb_kernel.rb +2 -0
- data/lib/merb/core_ext/merb_module.rb +41 -0
- data/lib/merb/core_ext/merb_numeric.rb +57 -5
- data/lib/merb/core_ext/merb_object.rb +172 -6
- data/lib/merb/generators/merb_app/merb_app.rb +15 -9
- data/lib/merb/merb_abstract_controller.rb +193 -0
- data/lib/merb/merb_constants.rb +26 -1
- data/lib/merb/merb_controller.rb +143 -234
- data/lib/merb/merb_dispatcher.rb +28 -20
- data/lib/merb/merb_drb_server.rb +2 -3
- data/lib/merb/merb_exceptions.rb +194 -49
- data/lib/merb/merb_handler.rb +34 -26
- data/lib/merb/merb_mail_controller.rb +200 -0
- data/lib/merb/merb_mailer.rb +33 -13
- data/lib/merb/merb_part_controller.rb +42 -0
- data/lib/merb/merb_plugins.rb +293 -0
- data/lib/merb/merb_request.rb +6 -4
- data/lib/merb/merb_router.rb +99 -65
- data/lib/merb/merb_server.rb +65 -21
- data/lib/merb/merb_upload_handler.rb +2 -1
- data/lib/merb/merb_view_context.rb +36 -15
- data/lib/merb/mixins/basic_authentication_mixin.rb +5 -5
- data/lib/merb/mixins/controller_mixin.rb +67 -28
- data/lib/merb/mixins/erubis_capture_mixin.rb +1 -8
- data/lib/merb/mixins/form_control_mixin.rb +280 -42
- data/lib/merb/mixins/render_mixin.rb +127 -45
- data/lib/merb/mixins/responder_mixin.rb +5 -7
- data/lib/merb/mixins/view_context_mixin.rb +260 -94
- data/lib/merb/session.rb +23 -0
- data/lib/merb/session/merb_ar_session.rb +28 -16
- data/lib/merb/session/merb_mem_cache_session.rb +108 -0
- data/lib/merb/session/merb_memory_session.rb +65 -20
- data/lib/merb/template/erubis.rb +22 -13
- data/lib/merb/template/haml.rb +5 -16
- data/lib/merb/template/markaby.rb +5 -3
- data/lib/merb/template/xml_builder.rb +17 -5
- data/lib/merb/test/merb_fake_request.rb +63 -0
- data/lib/merb/test/merb_multipart.rb +58 -0
- data/lib/tasks/db.rake +2 -0
- data/lib/tasks/merb.rake +20 -8
- metadata +24 -25
- data/examples/skeleton.tar +0 -0
data/lib/merb/merb_constants.rb
CHANGED
@@ -21,5 +21,30 @@ module Merb
|
|
21
21
|
HOUR = 60*60
|
22
22
|
DAY = HOUR*24
|
23
23
|
WEEK = DAY*7
|
24
|
+
MULTIPART_REGEXP = /\Amultipart\/form-data.*boundary=\"?([^\";,]+)/n.freeze
|
25
|
+
HTTP_COOKIE = 'HTTP_COOKIE'.freeze
|
26
|
+
QUERY_STRING = 'QUERY_STRING'.freeze
|
27
|
+
APPLICATION_JSON = 'application/json'.freeze
|
28
|
+
TEXT_JSON = 'text/x-json'.freeze
|
29
|
+
APPLICATION_XML = 'application/xml'.freeze
|
30
|
+
TEXT_XML = 'text/xml'.freeze
|
31
|
+
UPCASE_CONTENT_TYPE = 'CONTENT_TYPE'.freeze
|
32
|
+
CONTENT_TYPE = "Content-Type".freeze
|
33
|
+
LAST_MODIFIED = "Last-Modified".freeze
|
34
|
+
SLASH = "/".freeze
|
35
|
+
REQUEST_METHOD = "REQUEST_METHOD".freeze
|
36
|
+
GET = "GET".freeze
|
37
|
+
POST = "POST".freeze
|
38
|
+
HEAD = "HEAD".freeze
|
39
|
+
CONTENT_LENGTH = "CONTENT_LENGTH".freeze
|
40
|
+
HTTP_X_FORWARDED_FOR = "HTTP_X_FORWARDED_FOR".freeze
|
41
|
+
HTTP_IF_MODIFIED_SINCE = "HTTP_IF_MODIFIED_SINCE".freeze
|
42
|
+
HTTP_IF_NONE_MATCH = "HTTP_IF_NONE_MATCH".freeze
|
43
|
+
UPLOAD_ID = 'upload_id'.freeze
|
44
|
+
PATH_INFO="PATH_INFO".freeze
|
45
|
+
SCRIPT_NAME="SCRIPT_NAME".freeze
|
46
|
+
REQUEST_URI='REQUEST_URI'.freeze
|
47
|
+
REQUEST_PATH='REQUEST_PATH'.freeze
|
48
|
+
REMOTE_ADDR="REMOTE_ADDR".freeze
|
24
49
|
end
|
25
|
-
end
|
50
|
+
end
|
data/lib/merb/merb_controller.rb
CHANGED
@@ -1,297 +1,206 @@
|
|
1
1
|
require File.dirname(__FILE__)+'/mixins/controller_mixin'
|
2
|
-
require File.dirname(__FILE__)+'/mixins/render_mixin'
|
3
2
|
require File.dirname(__FILE__)+'/mixins/responder_mixin'
|
4
3
|
require File.dirname(__FILE__)+'/merb_request'
|
5
|
-
|
4
|
+
require File.dirname(__FILE__)+'/merb_exceptions'
|
5
|
+
require 'set'
|
6
6
|
module Merb
|
7
7
|
|
8
|
-
# All of your controllers will inherit from Merb::Controller. This
|
8
|
+
# All of your web controllers will inherit from Merb::Controller. This
|
9
9
|
# superclass takes care of parsing the incoming headers and body into
|
10
10
|
# params and cookies and headers. If the request is a file upload it will
|
11
11
|
# stream it into a tempfile and pass in the filename and tempfile object
|
12
12
|
# to your controller via params. It also parses the ?query=string and
|
13
13
|
# puts that into params as well.
|
14
|
-
class Controller
|
15
|
-
|
16
|
-
class_inheritable_accessor :
|
17
|
-
:_session_id_key,
|
18
|
-
:_template_extensions,
|
19
|
-
:_template_root,
|
20
|
-
:_layout_root
|
21
|
-
self._layout = :application
|
14
|
+
class Controller < AbstractController
|
15
|
+
|
16
|
+
class_inheritable_accessor :_session_id_key, :_session_expiry
|
22
17
|
self._session_id_key = :_session_id
|
23
|
-
self.
|
24
|
-
|
25
|
-
self._layout_root = File.expand_path(MERB_VIEW_ROOT / "layout")
|
26
|
-
|
18
|
+
self._session_expiry = Time.now + Merb::Const::WEEK * 2
|
19
|
+
|
27
20
|
include Merb::ControllerMixin
|
28
|
-
include Merb::RenderMixin
|
29
21
|
include Merb::ResponderMixin
|
30
|
-
|
31
|
-
|
22
|
+
include Merb::ControllerExceptions::HTTPErrors
|
23
|
+
|
24
|
+
class << self
|
25
|
+
def callable_actions
|
26
|
+
@callable_actions ||= Set.new(public_instance_methods - hidden_actions)
|
27
|
+
end
|
28
|
+
|
29
|
+
def hidden_actions
|
30
|
+
write_inheritable_attribute(:hidden_actions, Merb::Controller.public_instance_methods) unless read_inheritable_attribute(:hidden_actions)
|
31
|
+
read_inheritable_attribute(:hidden_actions)
|
32
|
+
end
|
33
|
+
|
34
|
+
# Hide each of the given methods from being callable as actions.
|
35
|
+
def hide_action(*names)
|
36
|
+
write_inheritable_attribute(:hidden_actions, hidden_actions | names.collect { |n| n.to_s })
|
37
|
+
end
|
38
|
+
|
39
|
+
def build(req, env, args, resp)
|
40
|
+
cont = new
|
41
|
+
cont.parse_request(req, env, args, resp)
|
42
|
+
cont
|
43
|
+
end
|
44
|
+
end
|
32
45
|
|
33
|
-
|
34
|
-
|
35
|
-
#
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
def initialize(request, env, args, response)
|
40
|
-
@env = MerbHash[env.to_hash]
|
41
|
-
@status, @method, @response, @headers = 200, (env[Mongrel::Const::REQUEST_METHOD]||Mongrel::Const::GET).downcase.to_sym, response,
|
46
|
+
# Parses the http request into params, headers and cookies that you can use
|
47
|
+
# in your controller classes. Also handles file uploads by writing a
|
48
|
+
# tempfile and passing a reference in params.
|
49
|
+
def parse_request(req, env, args, resp)
|
50
|
+
env = env.to_hash
|
51
|
+
@_status, method, @_response, @_headers = 200, (env[Merb::Const::REQUEST_METHOD]||Merb::Const::GET).downcase.to_sym, resp,
|
42
52
|
{'Content-Type' =>'text/html'}
|
43
|
-
cookies = query_parse(
|
44
|
-
querystring = query_parse(
|
53
|
+
cookies = query_parse(env[Merb::Const::HTTP_COOKIE], ';,')
|
54
|
+
querystring = query_parse(env[Merb::Const::QUERY_STRING])
|
45
55
|
|
46
|
-
if MULTIPART_REGEXP =~
|
47
|
-
querystring.update(parse_multipart(
|
48
|
-
elsif
|
49
|
-
if [
|
56
|
+
if Merb::Const::MULTIPART_REGEXP =~ env[Merb::Const::UPCASE_CONTENT_TYPE] && [:put,:post].include?(method)
|
57
|
+
querystring.update(parse_multipart(req, $1, env))
|
58
|
+
elsif [:post, :put].include?(method)
|
59
|
+
if [Merb::Const::APPLICATION_JSON, Merb::Const::TEXT_JSON].include?(env[Merb::Const::UPCASE_CONTENT_TYPE])
|
50
60
|
MERB_LOGGER.info("JSON Request")
|
51
|
-
json = JSON.parse(
|
52
|
-
json = MerbHash.new(json) if json.is_a? Hash
|
61
|
+
json = JSON.parse(req.read || "") || {}
|
53
62
|
querystring.update(json)
|
54
|
-
elsif [
|
55
|
-
querystring.update(Hash.from_xml(
|
63
|
+
elsif [Merb::Const::APPLICATION_XML, Merb::Const::TEXT_XML].include?(env[Merb::Const::UPCASE_CONTENT_TYPE])
|
64
|
+
querystring.update(Hash.from_xml(req.read).with_indifferent_access)
|
56
65
|
else
|
57
|
-
querystring.update(query_parse(
|
66
|
+
querystring.update(query_parse(req.read))
|
58
67
|
end
|
59
68
|
end
|
60
69
|
|
61
|
-
@
|
62
|
-
|
70
|
+
@_cookies, @_params = cookies.symbolize_keys!, querystring.update(args).symbolize_keys!
|
71
|
+
|
72
|
+
if @_params.key?(_session_id_key) && !Merb::Server.config[:session_id_cookie_only]
|
73
|
+
@_cookies[_session_id_key] = @_params[_session_id_key]
|
74
|
+
elsif @_params.key?(_session_id_key) && Merb::Server.config[:session_id_cookie_only]
|
75
|
+
# This condition allows for certain controller/action paths to allow a
|
76
|
+
# session ID to be passed in a query string. This is needed for Flash
|
77
|
+
# Uploads to work since flash will not pass a Session Cookie Recommend
|
78
|
+
# running session.regenerate after any controller taking advantage of
|
79
|
+
# this in case someone is attempting a session fixation attack
|
80
|
+
@_cookies[_session_id_key] = @_params[_session_id_key] if Merb::Server.config[:query_string_whitelist].include?("#{params[:controller]}/#{params[:action]}")
|
81
|
+
end
|
63
82
|
|
83
|
+
# Handle alternate HTTP method passed as _method parameter. Doesn't allow
|
84
|
+
# method to be overridden for :get unless Merb is in development mode.
|
85
|
+
#
|
86
|
+
# i.e. You can pass _method=put on the querystring if you are in
|
87
|
+
# development mode.
|
64
88
|
allow = [:post, :put, :delete]
|
65
89
|
allow << :get if MERB_ENV == 'development'
|
66
|
-
if @
|
67
|
-
|
90
|
+
if @_params.key?(:_method) && allow.include?(method)
|
91
|
+
method = @_params.delete(:_method).downcase.intern
|
68
92
|
end
|
69
|
-
@
|
70
|
-
|
93
|
+
@_request = Request.new(env, method, req)
|
71
94
|
MERB_LOGGER.info("Params: #{params.inspect}\nCookies: #{cookies.inspect}")
|
72
95
|
end
|
96
|
+
|
73
97
|
|
74
|
-
|
98
|
+
|
99
|
+
def dispatch(action=:index)
|
75
100
|
start = Time.now
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
else
|
90
|
-
raise MerbControllerError, "The before filter chain is broken dude. wtf?"
|
101
|
+
begin
|
102
|
+
if !self.class.callable_actions.include?(action.to_s)
|
103
|
+
raise NotFound
|
104
|
+
MERB_LOGGER.info "Action: #{action} not in callable_actions: #{self.class.callable_actions}"
|
105
|
+
else
|
106
|
+
setup_session
|
107
|
+
super(action)
|
108
|
+
finalize_session
|
109
|
+
end
|
110
|
+
rescue ControllerExceptions::Base => e
|
111
|
+
e.set_controller(self) # for access to session, params, etc
|
112
|
+
@_body = e.call_action
|
113
|
+
set_status(e.status)
|
91
114
|
end
|
92
|
-
|
93
|
-
|
94
|
-
MERB_LOGGER.info("Time spent in #{self.class}##{action} action: #{
|
115
|
+
|
116
|
+
@_benchmarks[:action_time] = Time.now - start
|
117
|
+
MERB_LOGGER.info("Time spent in #{self.class}##{action} action: #{@_benchmarks[:action_time]} seconds")
|
118
|
+
end
|
119
|
+
|
120
|
+
# Accessor for @_body. Please use status and never @status directly.
|
121
|
+
def body
|
122
|
+
@_body
|
95
123
|
end
|
96
124
|
|
97
|
-
#
|
98
|
-
|
99
|
-
|
100
|
-
"<html><body><h1>Filter Chain Halted!</h1></body></html>"
|
125
|
+
# Accessor for @_status. Please use status and never @_status directly.
|
126
|
+
def status
|
127
|
+
@_status
|
101
128
|
end
|
102
129
|
|
103
|
-
|
104
|
-
#
|
130
|
+
|
131
|
+
# Accessor for @_request. Please use request and never @_request directly.
|
105
132
|
def request
|
106
|
-
@
|
133
|
+
@_request
|
107
134
|
end
|
108
|
-
|
109
|
-
#
|
110
|
-
# never @params directly.
|
135
|
+
|
136
|
+
# Accessor for @_params. Please use params and never @_params directly.
|
111
137
|
def params
|
112
|
-
@
|
113
|
-
end
|
138
|
+
@_params
|
139
|
+
end
|
114
140
|
|
115
|
-
#
|
116
|
-
# never @cookies directly.
|
141
|
+
# Accessor for @_cookies. Please use cookies and never @_cookies directly.
|
117
142
|
def cookies
|
118
|
-
@
|
119
|
-
end
|
120
|
-
|
121
|
-
#
|
122
|
-
# never @headers directly.
|
143
|
+
@_cookies
|
144
|
+
end
|
145
|
+
|
146
|
+
# Accessor for @_headers. Please use headers and never @_headers directly.
|
123
147
|
def headers
|
124
|
-
@
|
148
|
+
@_headers
|
125
149
|
end
|
126
150
|
|
127
|
-
#
|
128
|
-
# never @session directly.
|
151
|
+
# Accessor for @_session. Please use session and never @_session directly.
|
129
152
|
def session
|
130
|
-
@
|
153
|
+
@_session
|
131
154
|
end
|
132
155
|
|
133
|
-
#
|
134
|
-
# never @response directly.
|
156
|
+
# Accessor for @_response. Please use response and never @_response directly.
|
135
157
|
def response
|
136
|
-
@
|
158
|
+
@_response
|
137
159
|
end
|
138
160
|
|
139
|
-
|
140
|
-
#
|
141
|
-
#
|
142
|
-
#
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
#
|
151
|
-
#
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
end
|
161
|
+
# Sends a mail from a MailController
|
162
|
+
#
|
163
|
+
# send_mail FooMailer, :bar, :from => "foo@bar.com", :to => "baz@bat.com"
|
164
|
+
#
|
165
|
+
# would send an email via the FooMailer's bar method.
|
166
|
+
#
|
167
|
+
# The mail_params hash would be sent to the mailer, and includes items
|
168
|
+
# like from, to subject, and cc. See
|
169
|
+
# Merb::MailController#dispatch_and_deliver for more details.
|
170
|
+
#
|
171
|
+
# The send_params hash would be sent to the MailController, and is
|
172
|
+
# available to methods in the MailController as <tt>params</tt>. If you do
|
173
|
+
# not send any send_params, this controller's params will be available to
|
174
|
+
# the MailController as <tt>params</tt>
|
175
|
+
def send_mail(klass, method, mail_params, send_params = nil)
|
176
|
+
klass.new(send_params || params, self).dispatch_and_deliver(method, mail_params)
|
156
177
|
end
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
# shared_accessor sets up a class instance variable that can
|
161
|
-
# be unique for each class but also inherits the shared attrs
|
162
|
-
# from its superclasses. Since @@class variables are almost
|
163
|
-
# global vars within an inheritance tree, we use
|
164
|
-
# @class_instance_variables instead
|
165
|
-
class_inheritable_accessor :before_filters
|
166
|
-
class_inheritable_accessor :after_filters
|
167
|
-
|
168
|
-
# calls a filter chain according to rules.
|
169
|
-
def call_filters(filter_set)
|
170
|
-
(filter_set || []).each do |(filter, rule)|
|
171
|
-
ok = false
|
172
|
-
if rule.has_key?(:only)
|
173
|
-
if rule[:only].include?(params[:action].intern)
|
174
|
-
ok = true
|
175
|
-
end
|
176
|
-
elsif rule.has_key?(:exclude)
|
177
|
-
if !rule[:exclude].include?(params[:action].intern)
|
178
|
-
ok = true
|
179
|
-
end
|
180
|
-
else
|
181
|
-
ok = true
|
182
|
-
end
|
183
|
-
if ok
|
184
|
-
case filter
|
185
|
-
when Symbol, String
|
186
|
-
send(filter)
|
187
|
-
when Proc
|
188
|
-
filter.call(self)
|
189
|
-
end
|
190
|
-
end
|
191
|
-
end
|
192
|
-
return :filter_chain_completed
|
193
|
-
end
|
194
178
|
|
195
|
-
#
|
196
|
-
# filters in your controllers. Filters can either be a symbol
|
197
|
-
# or string that corresponds to a method name to call, or a
|
198
|
-
# proc object. if it is a method name that method will be
|
199
|
-
# called and if it is a proc it will be called with an argument
|
200
|
-
# of self where self is the current controller object. When
|
201
|
-
# you use a proc as a filter it needs to take one parameter.
|
179
|
+
# Dispatches a PartController. Use like:
|
202
180
|
#
|
203
|
-
#
|
204
|
-
# before :some_filter
|
205
|
-
# before :authenticate, :exclude => [:login, :signup]
|
206
|
-
# before Proc.new {|c| c.some_method }, :only => :foo
|
181
|
+
# <%= part TodoPart => :list %>
|
207
182
|
#
|
208
|
-
#
|
209
|
-
#
|
210
|
-
# and :exclude will run for every action that is not listed.
|
183
|
+
# will instantiate a new TodoPart controller and call the :list action
|
184
|
+
# invoking the Part's before and after filters as part of the call.
|
211
185
|
#
|
212
|
-
#
|
213
|
-
# filter chain you use throw :halt . If throw is called with
|
214
|
-
# only one argument of :halt the return of the method filters_halted
|
215
|
-
# will be what is rendered to the view. You can overide filters_halted
|
216
|
-
# in your own controllers to control what it outputs. But the throw
|
217
|
-
# construct is much more powerful then just that. throw :halt can
|
218
|
-
# also take a second argument. Here is what that second arg can be
|
219
|
-
# and the behavior each type can have:
|
186
|
+
# returns a string containing the results of the Part controllers dispatch
|
220
187
|
#
|
221
|
-
#
|
222
|
-
#
|
223
|
-
# a string you can render a template or just use a plain string:
|
188
|
+
# You can compose parts easily as well, these two parts will stil be wrapped
|
189
|
+
# in the layout of the Foo controller:
|
224
190
|
#
|
225
|
-
#
|
226
|
-
#
|
227
|
-
#
|
191
|
+
# class Foo < Application
|
192
|
+
# def some_action
|
193
|
+
# wrap_layout(part(TodoPart => :new) + part(TodoPart => :list))
|
194
|
+
# end
|
195
|
+
#end
|
228
196
|
#
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
# throw :halt, :must_click_disclaimer
|
233
|
-
#
|
234
|
-
# If the second arg is a Proc, it will be called and its return
|
235
|
-
# value will be what is rendered to the browser:
|
236
|
-
# Proc:
|
237
|
-
# throw :halt, Proc.new {|c| c.access_denied }
|
238
|
-
# throw :halt, Proc.new {|c| Tidy.new(c.index) }
|
239
|
-
#
|
240
|
-
def self.before(filter, opts={})
|
241
|
-
raise(ArgumentError,
|
242
|
-
"You can specify either :only or :exclude but
|
243
|
-
not both at the same time for the same filter."
|
244
|
-
) if opts.has_key?(:only) && opts.has_key?(:exclude)
|
245
|
-
|
246
|
-
opts = shuffle_filters!(opts)
|
247
|
-
|
248
|
-
case filter
|
249
|
-
when Symbol, String, Proc
|
250
|
-
(self.before_filters ||= []) << [filter, opts]
|
251
|
-
else
|
252
|
-
raise(ArgumentError,
|
253
|
-
'filters need to be either a Symbol, String or a Proc'
|
254
|
-
)
|
255
|
-
end
|
256
|
-
end
|
257
|
-
|
258
|
-
# #after is a class method that allows you to specify after
|
259
|
-
# filters in your controllers. Filters can either be a symbol
|
260
|
-
# or string that corresponds to a method name or a proc object.
|
261
|
-
# if it is a method name that method will be called and if it
|
262
|
-
# is a proc it will be called with an argument of self. When
|
263
|
-
# you use a proc as a filter it needs to take one parameter.
|
264
|
-
# you can gain access to the response body like so:
|
265
|
-
# after Proc.new {|c| Tidy.new(c.body) }, :only => :index
|
266
|
-
#
|
267
|
-
def self.after(filter, opts={})
|
268
|
-
raise(ArgumentError,
|
269
|
-
"You can specify either :only or :exclude but
|
270
|
-
not both at the same time for the same filter."
|
271
|
-
) if opts.has_key?(:only) && opts.has_key?(:exclude)
|
272
|
-
|
273
|
-
opts = shuffle_filters!(opts)
|
274
|
-
|
275
|
-
case filter
|
276
|
-
when Symbol, Proc, String
|
277
|
-
(self.after_filters ||= []) << [filter, opts]
|
278
|
-
else
|
279
|
-
raise(ArgumentError,
|
280
|
-
'After filters need to be either a Symbol, String or a Proc'
|
281
|
-
)
|
197
|
+
def part(opts={})
|
198
|
+
res = opts.inject([]) do |memo,(klass,action)|
|
199
|
+
memo << klass.new(self).dispatch(action)
|
282
200
|
end
|
201
|
+
res.size == 1 ? res[0] : res
|
283
202
|
end
|
284
203
|
|
285
|
-
def self.shuffle_filters!(opts={})
|
286
|
-
if opts[:only] && opts[:only].is_a?(Symbol)
|
287
|
-
opts[:only] = [opts[:only]]
|
288
|
-
end
|
289
|
-
if opts[:exclude] && opts[:exclude].is_a?(Symbol)
|
290
|
-
opts[:exclude] = [opts[:exclude]]
|
291
|
-
end
|
292
|
-
return opts
|
293
|
-
end
|
294
|
-
|
295
204
|
end
|
296
205
|
|
297
206
|
end
|