Class: RocketIO::Controller
- Inherits:
-
Object
- Object
- RocketIO::Controller
- Extended by:
- Forwardable
- Defined in:
- lib/rocketio/controller.rb,
lib/rocketio/controller/cookies.rb,
lib/rocketio/controller/filters.rb,
lib/rocketio/controller/helpers.rb,
lib/rocketio/controller/sessions.rb,
lib/rocketio/controller/websocket.rb,
lib/rocketio/controller/middleware.rb,
lib/rocketio/controller/authorization.rb,
lib/rocketio/controller/authentication.rb,
lib/rocketio/controller/error_handlers.rb
Class Method Summary collapse
-
.alias_url(path) ⇒ Object
allow same controller to serve multiple URLs.
- .aliases ⇒ Object
- .api ⇒ Object
-
.basic_auth(*args, &block) ⇒ Object
easily restrict access to controller using basic auth.
- .call(env) ⇒ Object
- .define_basic_auth_methods(source = self) ⇒ Object
- .define_digest_auth_methods(source = self) ⇒ Object
- .define_error_handlers_methods(source = self) ⇒ Object
- .define_middleware_methods(source = self) ⇒ Object
- .define_sessions_methods(source = self) ⇒ Object
- .define_token_auth_methods(source = self) ⇒ Object
-
.digest_auth(*args, &block) ⇒ Object
easily restrict access to controller using digest auth.
- .dirname(*args) ⇒ Object
- .environment ⇒ Object
-
.error(code, &block) ⇒ Object
define error handlers.
-
.import(setup, from:) ⇒ Object
import some config from some controller.
- .inherited(base) ⇒ Object
-
.map(path) ⇒ Object
by default controllers will use underscored name for base URL.
- .method_added(meth) ⇒ Object
-
.sessions(pool = (noargs = true; nil), opts = {}) ⇒ Object
setup sessions.
-
.token_auth(*args, &block) ⇒ Object
easily restrict access to controller using token auth.
-
.url(*args) ⇒ String
build a URL from given chunks prefixing them with controller’s baseurl.
-
.use(w = nil, *a, &b) ⇒ Object
storing Rack middleware to be called when a request handled by controller.
Instance Method Summary collapse
-
#__error__(code, *args) ⇒ Object
call defined error handler with given arguments.
- #__parse_json_body__ ⇒ Object
- #after ⇒ Object
- #around ⇒ Object
-
#attachment(filename = nil, disposition = 'attachment') ⇒ Object
Set the Content-Disposition to “attachment” with the specified filename, instructing the user agents to prompt to save.
- #authentication_required? ⇒ Boolean
- #authorization_required? ⇒ Boolean
-
#back ⇒ Object
Sugar for redirect (example: redirect back).
- #basic_auth ⇒ Object
- #before ⇒ Object
-
#cache_control(*values) ⇒ Object
Specify response freshness policy for HTTP caches (Cache-Control header).
-
#call(env) ⇒ Rack::Response
call requested method.
-
#charset(charset) ⇒ Object
shorthand for content_type(charset: ‘something’).
-
#client_error? ⇒ Boolean
whether or not the status is set to 4xx.
-
#content_type(*args) ⇒ Object
returns, set or update content type.
-
#cookies ⇒ Object
shorthand for ‘request.cookies`, `response.set_cookie` and `response.delete_cookie`.
- #digest_auth ⇒ Object
- #env ⇒ Object
-
#error(code, *args) ⇒ Object
(also: #error!)
if there is a handler defined for given code it will be executed and the result used as body.
- #error_handlers ⇒ Object
-
#etag(value, options = {}) ⇒ Object
Set the response entity tag (HTTP ‘ETag’ header) and halt if conditional GET matches.
-
#expires(amount, *values) ⇒ Object
Set the Expires header and Cache-Control/max-age directive.
- #flash ⇒ Object
-
#halt(*args) ⇒ Object
stop executing any code and send response to browser.
-
#headers(hash = nil) ⇒ Object
Set multiple response headers with Hash.
-
#http_1_1? ⇒ Boolean
returns true for HTTP/1.1 requests.
-
#informational? ⇒ Boolean
whether or not the status is set to 1xx.
-
#initialize(requested_method = RocketIO::INDEX_METHOD, path_params = nil, params = nil) ⇒ Controller
constructor
A new instance of Controller.
- #invoke_after_filter(method = requested_method) ⇒ Object
-
#invoke_around_filter(method = requested_method, block) ⇒ Object
passing blocks tends to add some overhead so passing the proc as a common argument.
- #invoke_before_filter(method = requested_method) ⇒ Object
- #json? ⇒ Boolean
-
#last_modified(time) ⇒ Object
Set the last modified time of the resource (HTTP ‘Last-Modified’ header) and halt if conditional GET matches.
- #middleware ⇒ Object
-
#not_found? ⇒ Boolean
whether or not the status is set to 404.
- #params ⇒ Object
-
#pass(controller, *args) ⇒ Object
switch controller and halt with returned response.
- #path_params ⇒ Object
- #path_params_array ⇒ Object
- #permanent_redirect(uri) ⇒ Object
-
#redirect(uri) ⇒ Object
Halt processing and redirect to the URI provided.
-
#redirect? ⇒ Boolean
whether or not the status is set to 3xx.
- #request ⇒ Object
- #request_token_auth(realm = RocketIO::DEFAULT_TOKEN_AUTH_REALM) ⇒ Object
- #requested_method ⇒ Object
- #requested_method_arguments ⇒ Object
- #response ⇒ Object
-
#send_file(path, opts = {}) ⇒ Object
Use the contents of the file at
pathas the response body. -
#server_error? ⇒ Boolean
whether or not the status is set to 5xx.
- #sessions ⇒ Object
-
#success? ⇒ Boolean
whether or not the status is set to 2xx.
-
#time_for(value) ⇒ Object
Generates a Time object from the given value.
- #token_auth ⇒ Object
-
#uri(addr = nil, absolute = true, add_script_name = true) ⇒ Object
Generates the absolute URI for a given path in the app.
- #user? ⇒ Boolean
-
#validate_or_request_authentication_if_needed ⇒ Object
checks whether authentication is required and send an authorization request if credentials not present or invalid.
- #validate_or_request_authorization_if_needed ⇒ Object
- #validate_or_request_token_auth(realm = RocketIO::DEFAULT_TOKEN_AUTH_REALM, &block) ⇒ Object
- #validate_token_auth(&block) ⇒ Object
- #websocket? ⇒ Boolean
- #websocket_connection? ⇒ Boolean
- #websocket_response ⇒ Object
- #websocket_upgrade? ⇒ Boolean
- #xhr? ⇒ Boolean
Constructor Details
#initialize(requested_method = RocketIO::INDEX_METHOD, path_params = nil, params = nil) ⇒ Controller
Returns a new instance of Controller.
17 18 19 20 21 22 23 24 25 26 |
# File 'lib/rocketio/controller.rb', line 17 def initialize requested_method = RocketIO::INDEX_METHOD, path_params = nil, params = nil @__requested_method__ = requested_method.to_sym @__path_params_array__ = path_params || RocketIO::EMPTY_ARRAY @__path_params_array__.freeze if params @__params__ = indifferent_params(params) end end |
Class Method Details
.alias_url(path) ⇒ Object
allow same controller to serve multiple URLs
216 217 218 219 220 221 222 223 224 225 226 227 228 |
# File 'lib/rocketio/controller.rb', line 216 def alias_url path path = path.to_s path = if path =~ /\A\// path else if superclass == Object RocketIO.rootify_path(path) else RocketIO.rootify_path(superclass.url, path) end end.freeze aliases.push(path) end |
.aliases ⇒ Object
230 231 232 |
# File 'lib/rocketio/controller.rb', line 230 def aliases @__aliases__ ||= [] end |
.api ⇒ Object
130 131 132 |
# File 'lib/rocketio/controller.rb', line 130 def api @__api__ end |
.basic_auth(*args, &block) ⇒ Object
authorization is composable, that’s it, if superclass is protecting :a and current controller is protecting :b method, both :a and :b will be protected in current controller
easily restrict access to controller using basic auth
30 31 32 33 34 35 36 37 38 39 40 41 |
# File 'lib/rocketio/controller/authentication.rb', line 30 def self.basic_auth *args, &block opts = args.last.is_a?(Hash) ? args.pop : {} (args.any? ? args.map(&:to_sym) : [:*]).each do |method| (@__basic_auth__ ||= {})[method] = { class: Rack::Auth::Basic, arguments: [opts[:realm] || RocketIO::DEFAULT_AUTH_REALM].freeze, block: block, mock: RocketIO::HTTP_AUTHORIZATION_MOCKS[:basic] }.freeze end define_basic_auth_methods end |
.call(env) ⇒ Object
265 266 267 268 269 270 271 272 273 |
# File 'lib/rocketio/controller.rb', line 265 def call env path_params = env[RocketIO::PATH_INFO].sub(url, RocketIO::EMPTY_STRING).scan(RocketIO::PATH_SPLITTER) method = if path_params.any? && api[path_params[0].to_sym] path_params.slice!(0).to_sym else RocketIO::INDEX_METHOD end new(method, path_params).call(env) end |
.define_basic_auth_methods(source = self) ⇒ Object
43 44 45 46 47 48 49 50 51 |
# File 'lib/rocketio/controller/authentication.rb', line 43 def self.define_basic_auth_methods source = self prompts = (source.instance_variable_get(:@__basic_auth__) || {}).each_with_object(allocate.basic_auth.dup) do |(m,p),o| method = :"__basic_auth__#{m}__" api.delete define_method(method, &p[:block]) o[m] = p.merge(method: method).freeze end.freeze return if prompts.empty? api.delete define_method(:basic_auth) {prompts} end |
.define_digest_auth_methods(source = self) ⇒ Object
96 97 98 99 100 101 102 103 104 |
# File 'lib/rocketio/controller/authentication.rb', line 96 def self.define_digest_auth_methods source = self prompts = (source.instance_variable_get(:@__digest_auth__) || {}).each_with_object(allocate.digest_auth.dup) do |(m,p),o| method = :"__digest_auth__#{m}__" api.delete define_method(method, &p[:block]) o[m] = p.merge(method: method).freeze end.freeze return if prompts.empty? api.delete define_method(:digest_auth) {prompts} end |
.define_error_handlers_methods(source = self) ⇒ Object
38 39 40 41 42 43 44 45 46 47 |
# File 'lib/rocketio/controller/error_handlers.rb', line 38 def self.define_error_handlers_methods source = self handlers = (source.instance_variable_get(:@__error_handlers__) || {}).each_with_object({}) do |(code,block),o| o[code] = :"__#{code}_error_handler__" api.delete define_method(o[code], &block) end handlers.update(allocate.error_handlers) return if handlers.empty? handlers.freeze api.delete define_method(:error_handlers) {handlers} end |
.define_middleware_methods(source = self) ⇒ Object
24 25 26 27 28 |
# File 'lib/rocketio/controller/middleware.rb', line 24 def self.define_middleware_methods source = self middleware = ((source.instance_variable_get(:@__middleware__) || []) + allocate.middleware).uniq.freeze return if middleware.empty? api.delete define_method(:middleware) {middleware} end |
.define_sessions_methods(source = self) ⇒ Object
56 57 58 59 60 |
# File 'lib/rocketio/controller/sessions.rb', line 56 def self.define_sessions_methods source = self return unless source.instance_variables.include?(:@__sessions__) sessions = source.instance_variable_get(:@__sessions__) api.delete define_method(:sessions) {sessions} end |
.define_token_auth_methods(source = self) ⇒ Object
25 26 27 28 29 |
# File 'lib/rocketio/controller/authorization.rb', line 25 def self.define_token_auth_methods source = self prompts = allocate.token_auth.merge(source.instance_variable_get(:@__token_auth__) || {}).freeze return if prompts.empty? api.delete define_method(:token_auth) {prompts} end |
.digest_auth(*args, &block) ⇒ Object
easily restrict access to controller using digest auth
81 82 83 84 85 86 87 88 89 90 91 92 93 94 |
# File 'lib/rocketio/controller/authentication.rb', line 81 def self.digest_auth *args, &block opts = args.last.is_a?(Hash) ? args.pop : {} opts[:realm] ||= RocketIO::DEFAULT_AUTH_REALM opts[:opaque] ||= opts[:realm] (args.any? ? args.map(&:to_sym) : [:*]).each do |method| (@__digest_auth__ ||= {})[method] = { class: Rack::Auth::Digest::MD5, arguments: [opts].freeze, block: block, mock: RocketIO::HTTP_AUTHORIZATION_MOCKS[:digest] }.freeze end define_digest_auth_methods end |
.dirname(*args) ⇒ Object
261 262 263 |
# File 'lib/rocketio/controller.rb', line 261 def dirname *args ::File.join(@__dirname__, args.map!(&:to_s)) end |
.environment ⇒ Object
275 |
# File 'lib/rocketio/controller.rb', line 275 def environment; RocketIO.environment end |
.error(code, &block) ⇒ Object
define error handlers
30 31 32 33 34 35 36 |
# File 'lib/rocketio/controller/error_handlers.rb', line 30 def self.error code, &block code = code.to_i code > 0 || raise(ArgumentError, 'Error code should be a number') block || raise(ArgumentError, 'block missing') (@__error_handlers__ ||= {})[code] = block define_error_handlers_methods end |
.import(setup, from:) ⇒ Object
import some config from some controller
135 136 137 |
# File 'lib/rocketio/controller.rb', line 135 def import setup, from: __send__(:"define_#{setup}_methods", from) end |
.inherited(base) ⇒ Object
139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 |
# File 'lib/rocketio/controller.rb', line 139 def inherited base # registering a new controller RocketIO.controllers.push(base) base.instance_variable_set(:@__api__, self.api.dup) # new controller inherits all setups from superclass RocketIO::INHERITABLE_SETUPS.each {|s| base.import(s, from: self)} # removing superclass name from new controller name path = RocketIO.underscore(base.name.to_s.sub(self.name.to_s + '::', '').gsub('::', '/')) # new controller uses for URL its underscored name prefixed by superclass URL base.map RocketIO.rootify_path(url, path) # setting dirname for new controller base.instance_variable_set(:@__dirname__, RocketIO.caller_to_dirname(caller).freeze) end |
.map(path) ⇒ Object
if given URL starts with a slash it will ignore class name and set URL as is
by default controllers will use underscored name for base URL. this method allow to set a custom base URL.
192 193 194 195 196 197 198 199 200 201 202 203 |
# File 'lib/rocketio/controller.rb', line 192 def map path path = path.to_s @__url__ = if path =~ /\A\// path else if superclass == Object RocketIO.rootify_path(path) else RocketIO.rootify_path(superclass.url, path) end end.freeze end |
.method_added(meth) ⇒ Object
249 250 251 252 253 254 255 256 257 258 259 |
# File 'lib/rocketio/controller.rb', line 249 def method_added meth return if self == RocketIO::Controller return unless public_instance_methods(false).include?(meth) params, path_params = instance_method(meth).parameters.partition {|p| p[0] =~ /\Akey/} api[meth] = { path_params: RocketIO.path_params(path_params).freeze, params_accepted_as_last_argument?: params.any?, } end |
.sessions(pool = (noargs = true; nil), opts = {}) ⇒ Object
to disable sessions for some controller set sessions to nil or false
setup sessions. there are 2 setups out of the box:
-
- memcache
you can also use your own setup
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
# File 'lib/rocketio/controller/sessions.rb', line 36 def self.sessions pool = (noargs = true; nil), opts = {} @__sessions__ = if pool.nil? || pool == false nil else if pool == :cookies && opts[:secret].nil? opts[:secret] = ::Digest::MD5.hexdigest([Time.now.to_f, rand]*'') end pool = case pool when :cookies ::Rack::Session::Cookie when :memcache ::Rack::Session::Memcache else pool end [pool, opts] end define_sessions_methods end |
.token_auth(*args, &block) ⇒ Object
easily restrict access to controller using token auth
14 15 16 17 18 19 20 21 22 23 |
# File 'lib/rocketio/controller/authorization.rb', line 14 def self.token_auth *args, &block opts = args.last.is_a?(Hash) ? args.pop : {} (args.any? ? args.map(&:to_sym) : [:*]).each do |method| (@__token_auth__ ||= {})[method] = { realm: opts[:realm] || RocketIO::DEFAULT_TOKEN_AUTH_REALM.freeze, block: block } end define_token_auth_methods end |
.url(*args) ⇒ String
build a URL from given chunks prefixing them with controller’s baseurl
239 240 241 242 243 244 245 246 247 |
# File 'lib/rocketio/controller.rb', line 239 def url *args return @__url__ if args.empty? query = if args.last.is_a?(Hash) RocketIO::QUERY_PREFIX + ::Rack::Utils.build_nested_query(args.pop) else RocketIO::EMPTY_STRING end ::File.join(@__url__, args.map!(&:to_s)) + query end |
.use(w = nil, *a, &b) ⇒ Object
middleware is inheritable
storing Rack middleware to be called when a request handled by controller
19 20 21 22 |
# File 'lib/rocketio/controller/middleware.rb', line 19 def self.use w = nil, *a, &b (@__middleware__ ||= []).push(proc {|app| w.new(app, *a, &b)}) define_middleware_methods end |
Instance Method Details
#__error__(code, *args) ⇒ Object
call defined error handler with given arguments.
66 67 68 69 |
# File 'lib/rocketio/controller/error_handlers.rb', line 66 def __error__ code, *args error_handlers[code] || raise(NotImplementedError, 'No handler defined for %s error' % code.inspect) __send__(error_handlers[code], *args) end |
#__parse_json_body__ ⇒ Object
82 83 84 |
# File 'lib/rocketio/controller/helpers.rb', line 82 def __parse_json_body__ JSON.parse(env[RACK_INPUT].read) end |
#after ⇒ Object
113 |
# File 'lib/rocketio/controller/filters.rb', line 113 def after; RocketIO::EMPTY_HASH end |
#around ⇒ Object
112 |
# File 'lib/rocketio/controller/filters.rb', line 112 def around; RocketIO::EMPTY_HASH end |
#attachment(filename = nil, disposition = 'attachment') ⇒ Object
Set the Content-Disposition to “attachment” with the specified filename, instructing the user agents to prompt to save.
254 255 256 257 258 259 260 261 |
# File 'lib/rocketio/controller/helpers.rb', line 254 def (filename = nil, disposition = 'attachment') response[RocketIO::CONTENT_DISPOSITION] = disposition.to_s if filename response[RocketIO::CONTENT_DISPOSITION] << ('; filename="%s"' % File.basename(filename)) ext = File.extname(filename) content_type(ext) unless response[RocketIO::CONTENT_TYPE] or ext.empty? end end |
#authentication_required? ⇒ Boolean
126 127 128 129 130 131 |
# File 'lib/rocketio/controller/authentication.rb', line 126 def authentication_required? digest_auth[requested_method] || basic_auth[requested_method] || digest_auth[:*] || basic_auth[:*] end |
#authorization_required? ⇒ Boolean
51 52 53 |
# File 'lib/rocketio/controller/authorization.rb', line 51 def token_auth[requested_method] || token_auth[:*] end |
#back ⇒ Object
Sugar for redirect (example: redirect back)
410 411 412 |
# File 'lib/rocketio/controller/helpers.rb', line 410 def back request.referer end |
#basic_auth ⇒ Object
53 |
# File 'lib/rocketio/controller/authentication.rb', line 53 def basic_auth; RocketIO::EMPTY_HASH end |
#before ⇒ Object
111 |
# File 'lib/rocketio/controller/filters.rb', line 111 def before; RocketIO::EMPTY_HASH end |
#cache_control(*values) ⇒ Object
Specify response freshness policy for HTTP caches (Cache-Control header). Any number of non-value directives (:public, :private, :no_cache, :no_store, :must_revalidate, :proxy_revalidate) may be passed along with a Hash of value directives (:max_age, :min_stale, :s_max_age).
cache_control :public, :must_revalidate, :max_age => 60
=> Cache-Control: public, must-revalidate, max-age=60
See RFC 2616 / 14.9 for more on standard cache control directives: tools.ietf.org/html/rfc2616#section-14.9.1
299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 |
# File 'lib/rocketio/controller/helpers.rb', line 299 def cache_control *values if values.last.kind_of?(Hash) hash = values.pop hash.reject! { |k,v| v == false } hash.reject! { |k,v| values << k if v == true } else hash = {} end values.map! { |value| value.to_s.tr('_','-') } hash.each do |key, value| key = key.to_s.tr('_', '-') value = value.to_i if key == "max-age" values << "#{key}=#{value}" end response[RocketIO::CACHE_CONTROL] = values.join(', ') if values.any? end |
#call(env) ⇒ Rack::Response
call requested method
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
# File 'lib/rocketio/controller.rb', line 33 def call env @__env__ = env catch :__response__ do api[requested_method] || error(501) if error_handlers[500] begin __call__ rescue Exception => e error(500, e) end else __call__ end end end |
#charset(charset) ⇒ Object
shorthand for content_type(charset: ‘something’)
205 206 207 |
# File 'lib/rocketio/controller/helpers.rb', line 205 def charset charset content_type(charset: charset) end |
#client_error? ⇒ Boolean
whether or not the status is set to 4xx
53 54 55 |
# File 'lib/rocketio/controller/helpers.rb', line 53 def client_error? response.status.between? 400, 499 end |
#content_type(*args) ⇒ Object
returns, set or update content type. if called without args it will return current content type. if called with a single argument, given argument will be set as content type. if a type and hash given it will set brand new content type composed of given type and opts. if only a hash given it will update current content type with given opts.
if no content type is set it will use default one + given opts.
225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 |
# File 'lib/rocketio/controller/helpers.rb', line 225 def content_type *args return response[RocketIO::CONTENT_TYPE] if args.empty? params = args.last.is_a?(Hash) ? args.pop.map {|kv| kv.map!(&:to_s)}.to_h : {} default = params.delete('default') if type = args.first mime_type = mime_type(type) || default || raise(ArgumentError, "Unknown media type: %p" % type) else mime_type = response[RocketIO::CONTENT_TYPE] end mime_type ||= RocketIO::DEFAULT_CONTENT_TYPE mime_type, _params = mime_type.split(';') if _params params = _params.split(',').map! {|o| o.strip.split('=')}.to_h.merge!(params) end if params.any? mime_type << '; ' mime_type << params.map do |key, val| val = val.inspect if val =~ /[";,]/ [key, val]*'=' end.join(', ') end response[RocketIO::CONTENT_TYPE] = mime_type end |
#cookies ⇒ Object
shorthand for ‘request.cookies`, `response.set_cookie` and `response.delete_cookie`
22 23 24 |
# File 'lib/rocketio/controller/cookies.rb', line 22 def @__cookies__ ||= RocketIO::Cookies.new(request., response) end |
#digest_auth ⇒ Object
106 |
# File 'lib/rocketio/controller/authentication.rb', line 106 def digest_auth; RocketIO::EMPTY_HASH end |
#env ⇒ Object
51 52 53 |
# File 'lib/rocketio/controller.rb', line 51 def env @__env__ end |
#error(code, *args) ⇒ Object Also known as: error!
if there is a handler defined for given code it will be executed and the result used as body. otherwise the ‘error` behaves exactly as `halt`.
given args will be passed either to handler(if any defined) or to ‘halt`
56 57 58 59 |
# File 'lib/rocketio/controller/error_handlers.rb', line 56 def error code, *args error_handlers[code] || halt(code, *args) halt(code, __error__(code, *args)) end |
#error_handlers ⇒ Object
49 |
# File 'lib/rocketio/controller/error_handlers.rb', line 49 def error_handlers; RocketIO::EMPTY_HASH end |
#etag(value, options = {}) ⇒ Object
Set the response entity tag (HTTP ‘ETag’ header) and halt if conditional GET matches. The value argument is an identifier that uniquely identifies the current version of the resource. The kind argument indicates whether the etag should be used as a :strong (default) or :weak cache validator.
When the current request includes an ‘If-None-Match’ header with a matching etag, execution is immediately halted. If the request method is GET or HEAD, a ‘304 Not Modified’ response is sent.
380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 |
# File 'lib/rocketio/controller/helpers.rb', line 380 def etag value, = {} # Before touching this code, please double check RFC 2616 14.24 and 14.26. = {:kind => } unless Hash === kind = [:kind] || :strong new_resource = .fetch(:new_resource) { request.post? } unless RocketIO::ETAG_KINDS.include?(kind) raise ArgumentError, ":strong or :weak expected" end value = '"%s"' % value value = "W/#{value}" if kind == :weak response[RocketIO::ETAG] = value if response.successful? || response.not_modified? if etag_matches?(env[RocketIO::HTTP_IF_NONE_MATCH], new_resource) response.status = request.safe? ? 304 : 412 halt end if env[RocketIO::HTTP_IF_MATCH] unless etag_matches?(env[RocketIO::HTTP_IF_MATCH], new_resource) response.status = 412 halt end end end end |
#expires(amount, *values) ⇒ Object
Set the Expires header and Cache-Control/max-age directive. Amount can be an integer number of seconds in the future or a Time object indicating when the response should be considered “stale”. The remaining “values” arguments are passed to the #cache_control helper:
expires 500, :public, :must_revalidate
=> Cache-Control: public, must-revalidate, max-age=60
=> Expires: Mon, 08 Jun 2009 08:50:17 GMT
327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 |
# File 'lib/rocketio/controller/helpers.rb', line 327 def expires amount, *values values << {} unless values.last.kind_of?(Hash) if amount.is_a?(Integer) time = Time.now + amount.to_i max_age = amount else time = time_for amount max_age = time - Time.now end values.last.merge!(:max_age => max_age) cache_control(*values) response[RocketIO::EXPIRES] = time.httpdate end |
#flash ⇒ Object
160 161 162 |
# File 'lib/rocketio/controller/helpers.rb', line 160 def flash @__flash_proxy__ ||= RocketIO::Flash.new(session) end |
#halt(*args) ⇒ Object
stop executing any code and send response to browser.
accepts an arbitrary number of arguments. if first argument is a Rack::Response, halting right away using the first arg as response and ignore other args.
if first arg is a Array, updating current response using first array element as status, second to update headers and 3rd as body
if some arg is an Integer, it will be used as status code. if some arg is a Hash, it is treated as headers. any other args are treated as body.
if no args given it halts with current response.
131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 |
# File 'lib/rocketio/controller/helpers.rb', line 131 def halt *args args.each do |a| case a when Rack::Response throw(:__response__, a.finish) when Fixnum response.status = a when Array if a.size == 3 response.status = a[0] response.headers.update(a[1]) response.body = a[2] break else response.body = a end when Hash response.headers.update(a) else response.body = a end end throw(:__response__, response.finish) end |
#headers(hash = nil) ⇒ Object
Set multiple response headers with Hash.
199 200 201 202 |
# File 'lib/rocketio/controller/helpers.rb', line 199 def headers hash = nil response.headers.merge!(hash) if hash response.headers end |
#http_1_1? ⇒ Boolean
returns true for HTTP/1.1 requests
68 69 70 |
# File 'lib/rocketio/controller/helpers.rb', line 68 def http_1_1? env[RocketIO::HTTP_VERSION] == RocketIO::HTTP_1_1 end |
#informational? ⇒ Boolean
whether or not the status is set to 1xx
38 39 40 |
# File 'lib/rocketio/controller/helpers.rb', line 38 def informational? response.status.between? 100, 199 end |
#invoke_after_filter(method = requested_method) ⇒ Object
138 139 140 141 |
# File 'lib/rocketio/controller/filters.rb', line 138 def invoke_after_filter method = requested_method __send__(after[:*]) if after[:*] __send__(after[method]) if after[method] end |
#invoke_around_filter(method = requested_method, block) ⇒ Object
passing blocks tends to add some overhead so passing the proc as a common argument
122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 |
# File 'lib/rocketio/controller/filters.rb', line 122 def invoke_around_filter method = requested_method, block if around[:*] __send__ around[:*], proc { if around[method] __send__(around[method], block) else block.call end } elsif around[method] __send__(around[method], block) else block.call end end |
#invoke_before_filter(method = requested_method) ⇒ Object
115 116 117 118 |
# File 'lib/rocketio/controller/filters.rb', line 115 def invoke_before_filter method = requested_method __send__(before[:*]) if before[:*] __send__(before[method]) if before[method] end |
#json? ⇒ Boolean
76 77 78 79 80 |
# File 'lib/rocketio/controller/helpers.rb', line 76 def json? # using regex because Content-Type may look like "application/json;charset=UTF-8" # TODO: consider using Regexp#match? on Ruby 2.4 env[HTTP_CONTENT_TYPE] =~ RocketIO::APPLICATION_JSON_REGEXP end |
#last_modified(time) ⇒ Object
Set the last modified time of the resource (HTTP ‘Last-Modified’ header) and halt if conditional GET matches. The time argument is a Time, DateTime, or other object that responds to to_time.
When the current request includes an ‘If-Modified-Since’ header that is equal or later than the time specified, execution is immediately halted with a ‘304 Not Modified’ response.
351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 |
# File 'lib/rocketio/controller/helpers.rb', line 351 def last_modified time return unless time time = time_for(time) response[RocketIO::LAST_MODIFIED] = time.httpdate return if env[RocketIO::HTTP_IF_NONE_MATCH] if response.ok? && env[RocketIO::HTTP_IF_MODIFIED_SINCE] # compare based on seconds since epoch since = Time.httpdate(env[RocketIO::HTTP_IF_MODIFIED_SINCE]).to_i halt(304) if since >= time.to_i end if (response.successful? || response.precondition_failed?) && env[RocketIO::HTTP_IF_UNMODIFIED_SINCE] # compare based on seconds since epoch since = Time.httpdate(env[RocketIO::HTTP_IF_UNMODIFIED_SINCE]).to_i halt(412) if since < time.to_i end rescue ArgumentError end |
#middleware ⇒ Object
30 |
# File 'lib/rocketio/controller/middleware.rb', line 30 def middleware; RocketIO::EMPTY_ARRAY end |
#not_found? ⇒ Boolean
whether or not the status is set to 404
63 64 65 |
# File 'lib/rocketio/controller/helpers.rb', line 63 def not_found? response.status == 404 end |
#params ⇒ Object
86 87 88 |
# File 'lib/rocketio/controller.rb', line 86 def params @__params__ ||= indifferent_params(json? ? __parse_json_body__ : request.params) end |
#pass(controller, *args) ⇒ Object
switch controller and halt with returned response. any arguments will be passed to requested method.
95 96 97 |
# File 'lib/rocketio/controller/helpers.rb', line 95 def pass controller, *args halt controller.new(requested_method, args).call(env) end |
#path_params ⇒ Object
76 77 78 79 80 81 82 83 84 |
# File 'lib/rocketio/controller.rb', line 76 def path_params @__path_params__ ||= api[requested_method][:path_params].each_with_object({}) {|(m,r),o| o[m] = if r.min && r.max r.min == r.max ? path_params_array[r.min] : path_params_array[r] else path_params_array[r] end }.freeze end |
#path_params_array ⇒ Object
72 73 74 |
# File 'lib/rocketio/controller.rb', line 72 def path_params_array @__path_params_array__ end |
#permanent_redirect(uri) ⇒ Object
174 175 176 177 178 |
# File 'lib/rocketio/controller/helpers.rb', line 174 def permanent_redirect uri response.status = 301 response[RocketIO::LOCATION] = uri(uri.to_s) halt end |
#redirect(uri) ⇒ Object
Halt processing and redirect to the URI provided.
165 166 167 168 169 170 171 172 |
# File 'lib/rocketio/controller/helpers.rb', line 165 def redirect uri response.status = http_1_1? && !get? ? 303 : 302 # According to RFC 2616 section 14.30, "the field value consists of a # single absolute URI" response[RocketIO::LOCATION] = uri(uri.to_s) halt end |
#redirect? ⇒ Boolean
whether or not the status is set to 3xx
48 49 50 |
# File 'lib/rocketio/controller/helpers.rb', line 48 def redirect? response.status.between? 300, 399 end |
#request ⇒ Object
90 91 92 |
# File 'lib/rocketio/controller.rb', line 90 def request @__request__ ||= RocketIO::Request.new(env) end |
#request_token_auth(realm = RocketIO::DEFAULT_TOKEN_AUTH_REALM) ⇒ Object
47 48 49 |
# File 'lib/rocketio/controller/authorization.rb', line 47 def request_token_auth realm = RocketIO::DEFAULT_TOKEN_AUTH_REALM RocketIO::TokenAuth.authentication_request(realm) end |
#requested_method ⇒ Object
55 56 57 |
# File 'lib/rocketio/controller.rb', line 55 def requested_method @__requested_method__ end |
#requested_method_arguments ⇒ Object
59 60 61 62 63 64 65 66 67 68 69 70 |
# File 'lib/rocketio/controller.rb', line 59 def requested_method_arguments if api[requested_method][:params_accepted_as_last_argument?] [ *path_params_array, # creating a shallow params copy with symbolized keys # cause Ruby does not accept string keys for keyword arguments params.keys.each_with_object({}) {|k,o| o[k.to_sym] = params[k]} ] else path_params_array end end |
#response ⇒ Object
94 95 96 |
# File 'lib/rocketio/controller.rb', line 94 def response @__response__ ||= RocketIO::Response.new end |
#send_file(path, opts = {}) ⇒ Object
Use the contents of the file at path as the response body.
264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 |
# File 'lib/rocketio/controller/helpers.rb', line 264 def send_file path, opts = {} if opts[:type] or not response[RocketIO::CONTENT_TYPE] content_type(opts[:type] || File.extname(path), default: RocketIO::APPLICATION_OCTET_STREAM) end disposition = opts[:disposition] filename = opts[:filename] disposition = 'attachment' if disposition.nil? and filename filename = path if filename.nil? (filename, disposition) if disposition last_modified(opts[:last_modified]) if opts[:last_modified] file = Rack::File.new(nil) result = file.serving(request, path) result[1].each { |k,v| headers[k] ||= v } headers[RocketIO::CONTENT_LENGTH] = result[1][RocketIO::CONTENT_LENGTH] opts[:status] &&= Integer(opts[:status]) response.status = opts[:status] || result[0] response.body = result[2] halt rescue Errno::ENOENT error(404) end |
#server_error? ⇒ Boolean
whether or not the status is set to 5xx
58 59 60 |
# File 'lib/rocketio/controller/helpers.rb', line 58 def server_error? response.status.between? 500, 599 end |
#sessions ⇒ Object
62 |
# File 'lib/rocketio/controller/sessions.rb', line 62 def sessions; end |
#success? ⇒ Boolean
whether or not the status is set to 2xx
43 44 45 |
# File 'lib/rocketio/controller/helpers.rb', line 43 def success? response.status.between? 200, 299 end |
#time_for(value) ⇒ Object
Generates a Time object from the given value. Used by #expires and #last_modified.
416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 |
# File 'lib/rocketio/controller/helpers.rb', line 416 def time_for value if value.respond_to?(:to_time) value.to_time elsif value.is_a?(Time) value elsif value.respond_to?(:new_offset) # DateTime#to_time does the same on 1.9 d = value.new_offset 0 t = Time.utc(d.year, d.mon, d.mday, d.hour, d.min, d.sec + d.sec_fraction) t.getlocal elsif value.respond_to?(:mday) # Date#to_time does the same on 1.9 Time.local(value.year, value.mon, value.mday) elsif value.is_a? Numeric Time.at value else Time.parse value.to_s end rescue ArgumentError => boom raise boom rescue Exception raise ArgumentError, "unable to convert #{value.inspect} to a Time object" end |
#token_auth ⇒ Object
31 |
# File 'lib/rocketio/controller/authorization.rb', line 31 def token_auth; RocketIO::EMPTY_HASH end |
#uri(addr = nil, absolute = true, add_script_name = true) ⇒ Object
Generates the absolute URI for a given path in the app. Takes Rack routers and reverse proxies into account.
182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 |
# File 'lib/rocketio/controller/helpers.rb', line 182 def uri addr = nil, absolute = true, add_script_name = true return addr if addr && addr =~ /\A[A-z][A-z0-9\+\.\-]*:/ uri = [host = ""] if absolute host << "http#{'s' if request.secure?}://" if request.forwarded? or request.port != (request.secure? ? 443 : 80) host << request.host_with_port else host << request.host end end uri << request.script_name.to_s if add_script_name uri << (addr ? addr : request.path_info).to_s File.join(uri) end |
#user? ⇒ Boolean
108 |
# File 'lib/rocketio/controller/authentication.rb', line 108 def user?; env[RocketIO::REMOTE_USER] end |
#validate_or_request_authentication_if_needed ⇒ Object
checks whether authentication is required and send an authorization request if credentials not present or invalid
112 113 114 115 116 117 118 119 120 121 122 123 124 |
# File 'lib/rocketio/controller/authentication.rb', line 112 def validate_or_request_authentication_if_needed return unless auth = authentication_required? return unless prompt = auth[:class].new(proc {}, *auth[:arguments]) do |*a| self.__send__(auth[:method], *a) end.call( if RocketIO::HTTP_AUTHORIZATION_KEYS.detect {|key| env.has_key?(key)} env else env.merge(RocketIO::HTTP_AUTHORIZATION_KEYS.first => auth[:mock] % env[RocketIO::PATH_INFO]) end ) throw(:__response__, prompt) end |
#validate_or_request_authorization_if_needed ⇒ Object
33 34 35 36 37 |
# File 'lib/rocketio/controller/authorization.rb', line 33 def return unless auth = return if validate_token_auth(&auth[:block]) throw(:__response__, request_token_auth(auth[:realm])) end |
#validate_or_request_token_auth(realm = RocketIO::DEFAULT_TOKEN_AUTH_REALM, &block) ⇒ Object
39 40 41 |
# File 'lib/rocketio/controller/authorization.rb', line 39 def validate_or_request_token_auth realm = RocketIO::DEFAULT_TOKEN_AUTH_REALM, &block validate_token_auth(&block) || request_token_auth(realm) end |
#validate_token_auth(&block) ⇒ Object
43 44 45 |
# File 'lib/rocketio/controller/authorization.rb', line 43 def validate_token_auth &block RocketIO::TokenAuth.authenticate(env, &block) end |
#websocket? ⇒ Boolean
4 5 6 7 |
# File 'lib/rocketio/controller/websocket.rb', line 4 def websocket? @__is_websocket__ ||= websocket_connection? && websocket_upgrade? ? 1 : 0 @__is_websocket__ == 1 end |
#websocket_connection? ⇒ Boolean
9 10 11 |
# File 'lib/rocketio/controller/websocket.rb', line 9 def websocket_connection? env[RocketIO::HTTP_CONNECTION].to_s.downcase.split(/ *, */).include?(RocketIO::UPGRADE) end |
#websocket_response ⇒ Object
17 18 19 |
# File 'lib/rocketio/controller/websocket.rb', line 17 def websocket_response [-1, {}, []] end |
#websocket_upgrade? ⇒ Boolean
13 14 15 |
# File 'lib/rocketio/controller/websocket.rb', line 13 def websocket_upgrade? env[RocketIO::HTTP_UPGRADE].to_s.downcase == RocketIO::WEBSOCKET end |
#xhr? ⇒ Boolean
72 73 74 |
# File 'lib/rocketio/controller/helpers.rb', line 72 def xhr? env[RocketIO::HTTP_X_REQUESTED_WITH] == RocketIO::XML_HTTP_REQUEST end |