Class: Gin::Controller

Inherits:
Object
  • Object
show all
Extended by:
Mountable, GinClass
Includes:
Constants, Errorable, Filterable
Defined in:
lib/gin/controller.rb

Overview

Gin controllers follow only a few rules:

* ALL instance methods are actions (put your helper methods in a module or
  parent class not mounted to the app).
* Filters are defined by blocks passed to special class methods.
* Controller-level error handlers are defined by blocks passed to the 'error'
  class method.

Gin controller actions are any instance method defined on the controller
class. The HTTP response body takes the value of the method's return value.

If the instance method takes arguments, matching params will be assigned to
them. A required argument with a missing param triggers a Gin::BadRequest
error, resulting in a 400 response.

  class UserController < Gin::Controller
    # Get params id and email
    def show id, email=nil
      ...
    end
  end

Gin actions also support Ruby 2.0 keyed arguments, which are more flexible
for assigning default values when dealing with missing params.

  class UserController < Gin::Controller
    def show(id, email: nil, full: false)
      ...
    end
  end

Views are rendered by calling the 'view' method from a controller instance.
Views don't halt the action but return a String of the rendered view.
If a layout was set, the rendered view will include the layout.

  class UserController < Gin::Controller
    layout :user

    def show id
      # Renders <app.views_dir>/show_user.* with the layout <app.layouts_dir>/user.*
      view :show_user
    end

    def tos
      # Renders <app.views_dir>/tos.* with no layout
      view :tos, :layout => false
    end
  end

Constant Summary collapse

DEFAULT_ACTION_MAP =
{
  :index   => %w{GET /},
  :show    => %w{GET /:id},
  :new     => %w{GET /new},
  :create  => %w{POST /},
  :edit    => %w{GET /:id/edit},
  :update  => %w{PUT /:id},
  :destroy => %w{DELETE /:id}
}

Constants included from Constants

Gin::Constants::ASYNC_CALLBACK, Gin::Constants::CACHE_CTRL, Gin::Constants::CNT_DISPOSITION, Gin::Constants::CNT_LENGTH, Gin::Constants::CNT_TYPE, Gin::Constants::ENV_DEV, Gin::Constants::ENV_PROD, Gin::Constants::ENV_STAGE, Gin::Constants::ENV_TEST, Gin::Constants::EPOCH, Gin::Constants::ETAG, Gin::Constants::EXPIRES, Gin::Constants::FWD_FOR, Gin::Constants::FWD_HOST, Gin::Constants::GIN_APP, Gin::Constants::GIN_CTRL, Gin::Constants::GIN_ERRORS, Gin::Constants::GIN_PATH_PARAMS, Gin::Constants::GIN_RELOADED, Gin::Constants::GIN_ROUTE, Gin::Constants::GIN_STACK, Gin::Constants::GIN_STATIC, Gin::Constants::GIN_TARGET, Gin::Constants::GIN_TEMPLATES, Gin::Constants::GIN_TIMESTAMP, Gin::Constants::HOST_NAME, Gin::Constants::HTTP_VERSION, Gin::Constants::IF_MATCH, Gin::Constants::IF_MOD_SINCE, Gin::Constants::IF_NONE_MATCH, Gin::Constants::IF_UNMOD_SINCE, Gin::Constants::LAST_MOD, Gin::Constants::LOCATION, Gin::Constants::PATH_INFO, Gin::Constants::PRAGMA, Gin::Constants::QUERY_STRING, Gin::Constants::RACK_INPUT, Gin::Constants::REMOTE_ADDR, Gin::Constants::REMOTE_USER, Gin::Constants::REQ_METHOD, Gin::Constants::SERVER_NAME, Gin::Constants::SERVER_PORT, Gin::Constants::SESSION_SECRET

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Mountable

actions, controller_name, default_route_for, display_name, route_name_for, verify_mount!

Methods included from Errorable

#handle_error, #handle_status, included

Methods included from Filterable

#filter, included

Constructor Details

#initialize(app, env) ⇒ Controller

Returns a new instance of Controller.



200
201
202
203
204
205
206
# File 'lib/gin/controller.rb', line 200

def initialize app, env
  @app      = app
  @action   = nil
  @env      = env
  @request  = Gin::Request.new env
  @response = Gin::Response.new
end

Instance Attribute Details

#actionObject (readonly)

The action that the HTTP request resolved to, based on the App’s router.



194
195
196
# File 'lib/gin/controller.rb', line 194

def action
  @action
end

#appObject (readonly)

The Gin::App instance used by the controller. The App instance is meant for read-only use. Writes are not thread-safe and at your own risk.



185
186
187
# File 'lib/gin/controller.rb', line 185

def app
  @app
end

#envObject (readonly)

The Rack env hash.



197
198
199
# File 'lib/gin/controller.rb', line 197

def env
  @env
end

#requestObject (readonly)

Gin::Request instance representing the HTTP request.



188
189
190
# File 'lib/gin/controller.rb', line 188

def request
  @request
end

#responseObject (readonly)

Gin::Response instance representing the HTTP response.



191
192
193
# File 'lib/gin/controller.rb', line 191

def response
  @response
end

Class Method Details

.actionsObject

Array of action names for this controller.



87
88
89
# File 'lib/gin/controller.rb', line 87

def self.actions
  instance_methods(false).map{|a| a.to_sym }
end

.call(env) ⇒ Object

Call the Controller with an Rack env hash. Requires the hash to have the keys ‘gin.target’ with the action name as the second item of the array, and ‘gin.app’.



161
162
163
164
165
# File 'lib/gin/controller.rb', line 161

def self.call env
  inst = new(env[GIN_APP], env)
  env[GIN_CTRL] = inst
  inst.call_action(env[GIN_TARGET][1])
end

.content_type(new_type = nil) ⇒ Object

Set or get the default content type for this Gin::Controller. Default value is “text/html”. This attribute is inherited.



137
138
139
140
141
142
# File 'lib/gin/controller.rb', line 137

def self.content_type new_type=nil
  @content_type = new_type if new_type
  return @content_type if defined?(@content_type) && @content_type
  self.superclass.respond_to?(:content_type) &&
    self.superclass.content_type || "text/html"
end

.controller_name(new_name = nil) ⇒ Object

String representing the controller name. Underscores the class name and removes mentions of ‘controller’.

MyApp::FooController.controller_name
#=> "my_app/foo"

Note that when route names get autogenerated, the namespacing is dropped. If two routes have the same name, the last one defined wins.



101
102
103
104
# File 'lib/gin/controller.rb', line 101

def self.controller_name new_name=nil
  @ctrl_name = new_name if new_name
  @ctrl_name
end

.default_route_for(action) ⇒ Object

:nodoc:



118
119
120
# File 'lib/gin/controller.rb', line 118

def self.default_route_for action #:nodoc:
  DEFAULT_ACTION_MAP[action] || ['GET', "/#{action}"]
end

.display_name(action = nil) ⇒ Object

:nodoc:



128
129
130
# File 'lib/gin/controller.rb', line 128

def self.display_name action=nil #:nodoc:
  [self, action].compact.join("#")
end

.exec(app, env, &block) ⇒ Object

Execute arbitrary code in the context of a Gin::Controller instance. Returns a Rack response Array.



149
150
151
152
153
# File 'lib/gin/controller.rb', line 149

def self.exec app, env, &block
  inst = new(app, env)
  inst.invoke{ inst.instance_exec(&block) }
  inst.response.finish
end

.inherited(subclass) ⇒ Object



70
71
72
73
# File 'lib/gin/controller.rb', line 70

def self.inherited subclass
  subclass.setup
  super
end

.layout(name = nil) ⇒ Object

Get or set a layout for a given controller. Value can be a symbol or filepath. Layout file is expected to be in the Gin::App.layout_dir directory Defaults to the parent class layout, or Gin::App.layout.



174
175
176
177
178
# File 'lib/gin/controller.rb', line 174

def self.layout name=nil
  @layout = name if name
  return @layout if @layout
  return self.superclass.layout if self.superclass.respond_to?(:layout)
end

.route_name_for(action) ⇒ Object

:nodoc:



123
124
125
# File 'lib/gin/controller.rb', line 123

def self.route_name_for action #:nodoc:
  "#{action}_#{controller_name.sub(%r{^.*/},'')}".to_sym
end

.setupObject

:nodoc:



76
77
78
79
# File 'lib/gin/controller.rb', line 76

def self.setup   # :nodoc:
  @layout = nil
  @ctrl_name = Gin.underscore(self.to_s).gsub(/_?controller_?/,'')
end

Instance Method Details

#asset(path) ⇒ Object

Check if an asset exists. Returns the full system path to the asset if found, otherwise nil.



776
777
778
# File 'lib/gin/controller.rb', line 776

def asset path
  @app.asset path
end

#asset_path(name) ⇒ Object

Returns the HTTP path to the local asset.



756
757
758
759
760
761
762
763
764
765
766
767
768
769
# File 'lib/gin/controller.rb', line 756

def asset_path name
  fdpath = @app.asset(name)

  if fdpath && fdpath.start_with?(@app.assets_dir)
    if fdpath.start_with?(@app.public_dir)
      fdpath[@app.public_dir.length..-1]
    else
      fdpath[@app.assets_dir.length..-1]
    end
  else
    path = File.join('', name)
    [path, *@app.asset_version(name)].compact.join("?")
  end
end

#asset_url(name) ⇒ Object

Returns the url to an asset, including predefined asset cdn hosts if set.



746
747
748
749
750
# File 'lib/gin/controller.rb', line 746

def asset_url name
  host = @app.asset_host_for(name)
  return asset_path(name) if !host
  File.join(host, name)
end

#body(body = nil) ⇒ Object

Get or set the HTTP response body.



233
234
235
236
# File 'lib/gin/controller.rb', line 233

def body body=nil
  @response.body = body if body
  @response.body
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


686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
# File 'lib/gin/controller.rb', line 686

def cache_control *values
  if Hash === values.last
    hash = values.pop
    hash.reject!{|k,v| v == false || v == true && values << k }
  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].join('=')
  end

  @response[CACHE_CTRL] = values.join(', ') if values.any?
end

#call_action(action = nil) ⇒ Object

Calls the given or preset action and returns a Rack response Array.



212
213
214
215
216
217
218
# File 'lib/gin/controller.rb', line 212

def call_action action=nil
  action ||= @action
  invoke{ dispatch action }
  invoke{ handle_status(@response.status) }
  content_type self.class.content_type unless @response[CNT_TYPE]
  @response.finish
end

#configObject

Accessor for @app.config.



242
243
244
# File 'lib/gin/controller.rb', line 242

def config
  @app.config
end

#content_type(type = nil, params = {}) ⇒ Object

Get or set the HTTP response Content-Type header.

content_type :json
content_type 'application/json;charset=us-ascii'
content_type :json, charset: 'us-ascii'


261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
# File 'lib/gin/controller.rb', line 261

def content_type type=nil, params={}
  return @response[CNT_TYPE] unless type

  default   = params.delete(:default)
  mime_type = mime_type(type) || default
  raise "Unknown media type: %p" % type if mime_type.nil?

  mime_type = mime_type.dup
  unless params.include? :charset
    params[:charset] = params.delete('charset') || 'UTF-8'
  end

  params.delete :charset if mime_type.include? 'charset'
  unless params.empty?
    mime_type << (mime_type.include?(';') ? ', ' : ';')
    mime_type << params.map do |key, val|
      val = val.inspect if val =~ /[";,]/
      "#{key}=#{val}"
    end.join(', ')
  end

  @response[CNT_TYPE] = mime_type
end

#cookiesObject

Access the request cookies.



401
402
403
# File 'lib/gin/controller.rb', line 401

def cookies
  @request.cookies
end

Delete the response cookie with the given name. Does not affect request cookies.



427
428
429
# File 'lib/gin/controller.rb', line 427

def delete_cookie name
  @response.delete_cookie name
end

#dispatch(action) ⇒ Object

Dispatch the call to the action, calling before and after filers, and including error handling.



901
902
903
904
905
906
907
908
909
910
911
912
913
914
# File 'lib/gin/controller.rb', line 901

def dispatch action
  @action = action

  invoke do
    filter(*before_filters_for(action))
    args = action_arguments action
    __send__(action, *args)
  end

rescue => err
  invoke{ handle_error err }
ensure
  filter(*after_filters_for(action))
end

#error(code, body = nil) ⇒ Object

Halt processing and return the error status provided.



307
308
309
310
311
# File 'lib/gin/controller.rb', line 307

def error code, body=nil
  code, body     = 500, code if code.respond_to? :to_str
  @response.body = body unless body.nil?
  halt code
end

#etag(value, opts = {}) ⇒ Object

Set the ETag header. If the ETag was set in a previous request and matches the current one, halts the action and returns a 304 on GET and HEAD requests.



319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
# File 'lib/gin/controller.rb', line 319

def etag value, opts={}
  opts         = {:kind => opts} unless Hash === opts
  kind         = opts[:kind] || :strong
  new_resource = opts.fetch(:new_resource) { @request.post? }

  unless [:strong, :weak].include?(kind)
    raise ArgumentError, ":strong or :weak expected"
  end

  value = '"%s"' % value
  value = 'W/' + value if kind == :weak
  @response[ETAG] = value

  if (200..299).include?(status) || status == 304
    if etag_matches? @env[IF_NONE_MATCH], new_resource
      halt(@request.safe? ? 304 : 412)
    end

    if @env[IF_MATCH]
      halt 412 unless etag_matches? @env[IF_MATCH], new_resource
    end
  end
end

#etag_matches?(list, new_resource = @request.post?) ⇒ Boolean

:nodoc:

Returns:

  • (Boolean)


344
345
346
347
# File 'lib/gin/controller.rb', line 344

def etag_matches? list, new_resource=@request.post? #:nodoc:
  return !new_resource if list == '*'
  list.to_s.split(/\s*,\s*/).include? response[ETAG]
end

#expire_cache_controlObject

Sets Cache-Control, Expires, and Pragma headers to tell the browser not to cache the response.



737
738
739
740
# File 'lib/gin/controller.rb', line 737

def expire_cache_control
  @response[PRAGMA] = 'no-cache'
  expires EPOCH, :no_cache, :no_store, :must_revalidate, :max_age => 0
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


715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
# File 'lib/gin/controller.rb', line 715

def expires amount, *values
  values << {} unless Hash === values.last

  if Integer === amount
    time    = Time.now + amount.to_i
    max_age = amount
  else
    time    = String === amount ? Time.parse(amount) : amount
    max_age = time - Time.now
  end

  values.last.merge!(:max_age => max_age) unless values.last[:max_age]
  cache_control(*values)

  @response[EXPIRES] = time.httpdate
end

#h(obj) ⇒ Object

HTML-escape the given String.



954
955
956
# File 'lib/gin/controller.rb', line 954

def h obj
  CGI.escapeHTML obj.to_s
end

#halt(*resp) ⇒ Object

Stop the execution of an action and return the response. May be given a status code, string, header Hash, or a combination:

halt 400, "Badly formed request"
halt "Done early! WOOO!"
halt 302, {'Location' => 'http://example.com'}, "You are being redirected"


293
294
295
296
297
298
299
300
301
# File 'lib/gin/controller.rb', line 293

def halt *resp
  if @app.development?
    line = caller.find{|l| !l.start_with?(Gin::LIB_DIR) && !l.include?("/ruby/gems/")}
    logger << "[HALT] #{line}\n" if line
  end

  resp = resp.first if resp.length == 1
  throw :halt, resp
end

#headers(hash = nil) ⇒ Object

Set multiple response headers with Hash.



353
354
355
356
# File 'lib/gin/controller.rb', line 353

def headers hash=nil
  @response.headers.merge! hash if hash
  @response.headers
end

#html_error_page(err, code = nil) ⇒ Object

In development mode, returns an HTML page displaying the full error and backtrace, otherwise shows a generic error page.

Production error pages are first looked for in the public directory as <status>.html or 500.html. If none is found, falls back on Gin’s internal error html pages.



925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
# File 'lib/gin/controller.rb', line 925

def html_error_page err, code=nil
  if @app.development?
    backtrace = err.backtrace || ['No backtrace :(']
    fulltrace = backtrace.join("\n")
    fulltrace = "<pre>#{h(fulltrace)}</pre>"

    apptrace  = Gin.app_trace(backtrace).join("\n")
    apptrace  = "<pre>#{h(apptrace)}</pre>" unless apptrace.empty?

    DEV_ERROR_HTML %
      [h(err.class), h(err.class), h(err.message), apptrace, fulltrace]

  else
    code ||= status
    filepath = asset("#{code}.html") || asset("500.html")

    unless filepath
      filepath = File.join(Gin::PUBLIC_DIR, "#{code}.html")
      filepath = File.join(Gin::PUBLIC_DIR, "500.html") if !File.file?(filepath)
    end

    File.open(filepath, "rb")
  end
end

#invokeObject

Taken from Sinatra.

Run the block with ‘throw :halt’ support and apply result to the response.



882
883
884
885
886
887
888
889
890
891
892
893
894
# File 'lib/gin/controller.rb', line 882

def invoke
  res = catch(:halt) { yield }
  res = [res] if Fixnum === res || String === res
  if Array === res && Fixnum === res.first
    res = res.dup
    status(res.shift)
    body(res.pop)
    headers(*res)
  elsif res.respond_to? :each
    body res
  end
  nil # avoid double setting the same response tuple twice
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 or httpdate.



644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
# File 'lib/gin/controller.rb', line 644

def last_modified time
  return unless time

  time = if Integer === time
           Time.at(time)
         elsif time.respond_to?(:to_time)
           time.to_time
         elsif !time.is_a?(Time)
           Time.parse time.to_s
         else
           time
         end

  @response[LAST_MOD] = time.httpdate
  return if @env[IF_NONE_MATCH]

  if status == 200 && @env[IF_MOD_SINCE]
    # compare based on seconds since epoch
    since = Time.httpdate(@env[IF_MOD_SINCE]).to_i
    halt 304 if since >= time.to_i
  end

  if @env[IF_UNMOD_SINCE] &&
    ((200..299).include?(status) || status == 412)

    # compare based on seconds since epoch
    since = Time.httpdate(@env[IF_UNMOD_SINCE]).to_i
    halt 412 if since < time.to_i
  end
rescue ArgumentError
end

#layoutObject

Value of the layout to use for rendering. See also Gin::Controller.layout and Gin::App.layout.



785
786
787
# File 'lib/gin/controller.rb', line 785

def layout
  self.class.layout || @app.layout
end

#loggerObject

Accessor for main application logger.



377
378
379
# File 'lib/gin/controller.rb', line 377

def logger
  @app.logger
end

#mime_type(type) ⇒ Object

Get the normalized mime-type matching the given input.



250
251
252
# File 'lib/gin/controller.rb', line 250

def mime_type type
  @app.mime_type type
end

#paramsObject

Get the request params.



385
386
387
# File 'lib/gin/controller.rb', line 385

def params
  @request.params
end

#path_to(*args) ⇒ Object

Build a path to the given controller and action or route name, with any expected params. If no controller is specified and the current controller responds to the symbol given, uses the current controller for path lookup.

path_to FooController, :show, :id => 123
#=> "/foo/123"

# From FooController
path_to :show, :id => 123
#=> "/foo/123"

# Default named route
path_to :show_foo, :id => 123
#=> "/foo/123"


449
450
451
452
453
454
# File 'lib/gin/controller.rb', line 449

def path_to *args
  return "#{args[0]}#{"?" << Gin.build_query(args[1]) if args[1]}" if String === args[0]
  args.unshift(self.class) if Symbol === args[0] &&
                              self.class.actions.include?(args[0])
  @app.router.path_to(*args)
end

#redirect(uri, *args) ⇒ Object

Send a 301, 302, or 303 redirect and halt. Supports passing a full URI, partial path.

redirect "http://google.com"
redirect "/foo"
redirect "/foo", 301, "You are being redirected..."
redirect to(MyController, :action, :id => 123)
redirect to(:show_foo, :id => 123)


507
508
509
510
511
512
513
514
515
516
# File 'lib/gin/controller.rb', line 507

def redirect uri, *args
  if @env[HTTP_VERSION] == 'HTTP/1.1' && @env[REQ_METHOD] != 'GET'
    status 303
  else
    status 302
  end

  @response[LOCATION] = url_to(uri.to_s)
  halt(*args)
end

#reroute(*args) ⇒ Object

Unlike Gin::Controller#rewrite, the reroute method forwards the current request and params to the provided controller and/or action, or named route. Halts further execution in the current action. Raises RouterError if a given named route isn’t found in the app’s routes.

reroute MyController, :action
#=> Executes MyController#action

reroute MyController, :show, :id => 123
#=> Executes MyController#action with the given params merged to
#=> the current params.

reroute :show_foo
#=> Executes the current controller's :show_foo action, or if missing
#=> the controller and action for the :show_foo named route.

# Reroute with the given headers.
reroute :show_foo, {}, 'HTTP_X_CUSTOM_HEADER' => 'foo'


584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
# File 'lib/gin/controller.rb', line 584

def reroute *args
  args.unshift(self.class) if Symbol === args[0] &&
                              self.class.actions.include?(args[0])
  nheaders = args.pop if Hash === args.last && Hash === args[-2] && args[-2] != args[-1]
  nparams  = args.pop if Hash === args.last

  if Class === args[0]
    ctrl_klass, naction = args[0..1]
  else
    route = @app.router.route_to(*args)
    ctrl_klass, naction = route.target
  end

  nenv = @env.merge(nheaders || {})
  nenv[GIN_PATH_PARAMS] = {}
  ctrl = ctrl_klass.new(@app, nenv)

  ctrl.params.merge!(params)
  ctrl.params.merge!(nparams) if nparams

  halt(*ctrl.call_action(naction))
end

#rewrite(*args) ⇒ Object

Halt execution of the current controller action and create a new request to another action and/or controller, or path.

Raises Gin::RouterError if the controller and action don’t have a route. Returns a 404 response if an unrecognized path is given.

The rewrite method acts just as if a request had been sent from the client, and will re-run any of the in-app middleware. If the app is itself running as middleware, you may use rewrite to pass a request down to the next item in the stack by specifying a path not supported by the app.

Supports the same arguments at the Gin::Controller#url_to method.

rewrite MyController, :action
#=> Calls app with route for MyController#action

rewrite MyController, :show, :id => 123
#=> Calls app with route for MyController#action with the given params

rewrite :show_foo
#=> Calls app with route for the current controller's :show_foo action,
#=> or if missing the controller and action for the :show_foo named route.

# Rewrite and execute the request with the given headers.
rewrite :show_foo, 'HTTP_X_CUSTOM_HEADER' => 'foo'
rewrite :show_foo, params, 'HTTP_X_CUSTOM_HEADER' => 'foo'

# Rewrite to an arbitrary path.
rewrite '/path/to/something/else', {}, 'REQUEST_METHOD' => 'POST'

Note that params are not forwarded with the rewrite call. The app considers this to be a completely different request, which means all params required must be passed explicitely.

Streamed and IO request content is also ignored unless it is explicitely assigned to the ‘rack.input’ (as a part of the headers argument).



557
558
559
560
561
# File 'lib/gin/controller.rb', line 557

def rewrite *args
  args.unshift(self.class) if Symbol === args[0] &&
                              self.class.actions.include?(args[0])
  halt(*@app.rewrite!(@env, *args))
end

#send_file(path, opts = {}) ⇒ Object

Assigns a file to the response body and halts the execution of the action. Produces a 404 response if no file is found.



612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
# File 'lib/gin/controller.rb', line 612

def send_file path, opts={}
  if opts[:type] || !@response[CNT_TYPE]
    content_type opts[:type] || File.extname(path),
                  :default => 'application/octet-stream'
  end

  disposition = opts[:disposition]
  filename    = opts[:filename]
  disposition = 'attachment'        if disposition.nil? && filename
  filename    = File.basename(path) if filename.nil?

  if disposition
    @response[CNT_DISPOSITION] =
      "%s; filename=\"%s\"" % [disposition, filename]
  end

  last_modified opts[:last_modified] || File.mtime(path).httpdate
  halt 200 if @request.head?

  @response[CNT_LENGTH] = File.size?(path).to_s
  halt 200, File.open(path, "rb")

rescue Errno::ENOENT
  halt 404
end

#sessionObject

Access the request session.



393
394
395
# File 'lib/gin/controller.rb', line 393

def session
  @request.session
end

Set a cookie on the Rack response.

set_cookie "mycookie", "FOO", :expires => 600, :path => "/"
set_cookie "mycookie", :expires => 600


412
413
414
415
416
417
418
419
420
# File 'lib/gin/controller.rb', line 412

def set_cookie name, value=nil, opts={}
  if Hash === value
    opts = value
  else
    opts[:value] = value
  end

  @response.set_cookie name, opts
end

#status(code = nil) ⇒ Object

Set or get the HTTP response status code.



224
225
226
227
# File 'lib/gin/controller.rb', line 224

def status code=nil
  @response.status = code if code
  @response.status
end

#stream(keep_open = false, &block) ⇒ Object

Assigns a Gin::Stream to the response body, which is yielded to the block. The block execution is delayed until the action returns.

stream do |io|
  file = File.open "somefile", "rb"
  io << file.read(1024) until file.eof?
  file.close
end


368
369
370
371
# File 'lib/gin/controller.rb', line 368

def stream keep_open=false, &block
  scheduler = env[ASYNC_CALLBACK] ? EventMachine : Gin::Stream
  body Gin::Stream.new(scheduler, keep_open){ |out| yield(out) }
end

#template_path(template, is_layout = false) ⇒ Object

Returns the path to where the template is expected to be.

template_path :foo
#=> "<views_dir>/foo"

template_path "sub/foo"
#=> "<views_dir>/sub/foo"

template_path "sub/foo", :layout
#=> "<layouts_dir>/sub/foo"

template_path "/other/foo"
#=> "<root_dir>/other/foo"


804
805
806
807
808
809
810
811
812
813
814
815
816
# File 'lib/gin/controller.rb', line 804

def template_path template, is_layout=false
  dir = if template.to_s[0] == ?/
          @app.root_dir
        elsif is_layout
          @app.layouts_dir
        else
          @app.views_dir
        end

  path = File.join(dir, template.to_s)
  path.gsub!('*', controller_name)
  File.expand_path(path)
end

#url_to(*args) ⇒ Object Also known as: to



476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
# File 'lib/gin/controller.rb', line 476

def url_to *args
  path = path_to(*args)

  return path if path =~ /\A[A-z][A-z0-9\+\.\-]*:/

  uri  = [host = ""]
  host << "http#{'s' if @request.ssl?}://"

  if @request.forwarded? || @request.port != (@request.ssl? ? 443 : 80)
    host << @request.host_with_port
  else
    host << @request.host
  end

  uri << @request.script_name.to_s
  uri << path
  File.join uri
end

#view(template, opts = {}, &block) ⇒ Object

Render a template with the given view template. Options supported:

:locals

Hash - local variables used in template

:layout

Symbol/String - a custom layout to use

:scope

Object - The scope in which to render the template: default self

:content_type

Symbol/String - Content-Type header to set

:engine

String - Tilt rendering engine to use

:layout_engine

String - Tilt layout rendering engine to use

The template argument may be a String or a Symbol. By default the template location will be looked for under Gin::App.views_dir, but the directory may be specified as any directory under Gin::App.root_dir by using the ‘/’ prefix:

view 'foo/template'
#=> Renders file "<views_dir>/foo/template"

view '/foo/template'
#=> Renders file "<root_dir>/foo/template"

# Render without layout
view 'foo/template', layout: false


843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
# File 'lib/gin/controller.rb', line 843

def view template, opts={}, &block
  content_type(opts.delete(:content_type)) if opts[:content_type]

  scope  = opts[:scope]  || self
  locals = opts[:locals] || {}

  template   = template_path(template)
  v_template = @app.template_for template, opts[:engine]
  raise Gin::TemplateMissing, "No such template `#{template}'" unless v_template

  if opts[:layout] != false
    r_layout   = template_path((opts[:layout] || layout), true)
    r_template = @app.template_for r_layout, opts[:layout_engine] if r_layout
  end

  if !@response[CNT_TYPE]
    mime_type = v_template.class.default_mime_type ||
      r_template && r_template.class.default_mime_type
    content_type(mime_type) if mime_type
  end

  @env[GIN_TEMPLATES] ||= []

  if r_template
    @env[GIN_TEMPLATES] << r_template.file << v_template.file
    r_template.render(scope, locals){
      v_template.render(scope, locals, &block) }
  else
    @env[GIN_TEMPLATES] << v_template.file
    v_template.render(scope, locals, &block)
  end
end