Class: Merb::AbstractController

Inherits:
Object
  • Object
show all
Includes:
InlineTemplates, RenderMixin
Defined in:
lib/merb-core/controller/abstract_controller.rb

Direct Known Subclasses

Controller

Constant Summary collapse

FILTER_OPTIONS =
[:only, :exclude, :if, :unless, :with]

Constants included from ControllerExceptions

ControllerExceptions::STATUS_CODES

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from RenderMixin

#_get_layout, #_handle_options!, #_template_for, #_template_method_for, #append_content, #catch_content, #clear_content, #display, included, #partial, #render, #throw_content, #thrown_content?

Constructor Details

#initialize(*args) ⇒ AbstractController

This will initialize the controller, it is designed to be overridden in subclasses (like MerbController)

Parameters

*args

The args are ignored in this class, but we need this so that subclassed initializes can have parameters

:api: private


259
260
261
262
# File 'lib/merb-core/controller/abstract_controller.rb', line 259

def initialize(*args)
  @_benchmarks = {}
  @_caught_content = {}
end

Instance Attribute Details

#_benchmarksObject

:api: plugin


110
111
112
# File 'lib/merb-core/controller/abstract_controller.rb', line 110

def _benchmarks
  @_benchmarks
end

#_thrown_contentObject

:api: private


112
113
114
# File 'lib/merb-core/controller/abstract_controller.rb', line 112

def _thrown_content
  @_thrown_content
end

#action_nameObject

:api: plugin


110
111
112
# File 'lib/merb-core/controller/abstract_controller.rb', line 110

def action_name
  @action_name
end

#bodyObject

:api: plugin


110
111
112
# File 'lib/merb-core/controller/abstract_controller.rb', line 110

def body
  @body
end

#content_typeObject

Stub so content-type support in RenderMixin doesn't throw errors :api: private


116
117
118
# File 'lib/merb-core/controller/abstract_controller.rb', line 116

def content_type
  @content_type
end

Class Method Details

._reset_template_rootsObject

Reset the template root based on the @_template_root ivar.

:api: private


203
204
205
# File 'lib/merb-core/controller/abstract_controller.rb', line 203

def self._reset_template_roots
  self.template_roots = [[self._template_root, :_template_location]]
end

._template_root=(root) ⇒ Object

Resets the template roots to the template root passed in.

Parameters

root<~to_s>

The new path to set the template root to.

:api: public


195
196
197
198
# File 'lib/merb-core/controller/abstract_controller.rb', line 195

def self._template_root=(root)
  @_template_root = root
  _reset_template_roots
end

._template_rootsObject


213
214
215
# File 'lib/merb-core/controller/abstract_controller.rb', line 213

def self._template_roots
  self.template_roots || _reset_template_roots
end

._template_roots=(roots) ⇒ Object


223
224
225
# File 'lib/merb-core/controller/abstract_controller.rb', line 223

def self._template_roots=(roots)
  self.template_roots = roots
end

.after(filter = nil, opts = {}, &block) ⇒ Object

Adds a filter to the after filter chain

Parameters

filter<Symbol, Proc>

The filter to add. Defaults to nil.

opts<Hash>

Filter options (see class documentation under Filter Options).

&block

A block to use as a filter if filter is nil.

Notes

If the filter already exists, its options will be replaced with opts.;

:api: public


446
447
448
# File 'lib/merb-core/controller/abstract_controller.rb', line 446

def self.after(filter = nil, opts = {}, &block)
  add_filter(self._after_filters, filter || block, opts)
end

.before(filter = nil, opts = {}, &block) ⇒ Object

Adds a filter to the before filter chain.

Parameters

filter<Symbol, Proc>

The filter to add. Defaults to nil.

opts<Hash>

Filter options (see class documentation under Filter Options).

&block

A block to use as a filter if filter is nil.

Notes

If the filter already exists, its options will be replaced with opts.

:api: public


462
463
464
# File 'lib/merb-core/controller/abstract_controller.rb', line 462

def self.before(filter = nil, opts = {}, &block)
  add_filter(self._before_filters, filter || block, opts)
end

.controller_nameObject

Returns

String

The controller name in path form, e.g. “admin/items”.

:api: public


132
# File 'lib/merb-core/controller/abstract_controller.rb', line 132

def self.controller_name() @controller_name ||= self.name.to_const_path end

.inherited(klass) ⇒ Object

Parameters

klass<Merb::AbstractController>

The controller that is being inherited from Merb::AbstractController

:api: private


241
242
243
244
245
246
247
248
249
250
251
252
# File 'lib/merb-core/controller/abstract_controller.rb', line 241

def self.inherited(klass)
  _abstract_subclasses << klass.to_s
  helper_module_name = klass.to_s =~ /^(#|Merb::)/ ? "#{klass}Helper" : "Merb::#{klass}Helper"
  # support for unnamed module like "#<Class:0xa2e5e50>::TestController"
  helper_module_name.gsub!(/(::)|[:#<>]/, "\\1")

  Object.make_module helper_module_name
  klass.class_eval <<-HERE
    include Object.full_const_get("#{helper_module_name}") rescue nil
  HERE
  super
end

.skip_after(filter) ⇒ Object

Removes a filter from the after filter chain. This removes the filter from the filter chain for the whole controller and does not take any options.

Parameters

filter<Symbol, String>

A filter name to skip.

:api: public


474
475
476
# File 'lib/merb-core/controller/abstract_controller.rb', line 474

def self.skip_after(filter)
  skip_filter(self._after_filters, filter)
end

.skip_before(filter) ⇒ Object

Removes a filter from the before filter chain. This removes the filter from the filter chain for the whole controller and does not take any options.

Parameters

filter<Symbol, String>

A filter name to skip.

:api: public


486
487
488
# File 'lib/merb-core/controller/abstract_controller.rb', line 486

def self.skip_before(filter)
  skip_filter(self._before_filters , filter)
end

.subclasses_listObject

Returns the list of classes that have specifically subclassed AbstractController.

Does not include all decendents.

Returns

Set

The subclasses.

:api: private


234
# File 'lib/merb-core/controller/abstract_controller.rb', line 234

def self.subclasses_list() _abstract_subclasses end

Instance Method Details

#_absolute_template_location(template, type) ⇒ Object

The location to look for a template - override this method for particular behaviour.

Parameters

template<String>

The absolute path to a template - without template extension.

type<~to_s>

The mime-type of the template that will be rendered. Defaults to being called with nil.

:api: public


184
185
186
# File 'lib/merb-core/controller/abstract_controller.rb', line 184

def _absolute_template_location(template, type)
  template
end

#_call_action(action) ⇒ Object

This method exists to provide an overridable hook for ActionArgs. It uses #send to call the action method.

Parameters

action<~to_s>

the action method to dispatch to

:api: plugin


320
321
322
# File 'lib/merb-core/controller/abstract_controller.rb', line 320

def _call_action(action)
  send(action)
end

#_call_filter_for_action?(rule, action_name) ⇒ Boolean

Determine whether the filter should be called for the current action using :only and :exclude.

Parameters

rule<Hash>

Rules for the filter (see below).

action_name<~to_s>

The name of the action to be called.

Options (rule)

:only<Array>

Optional list of actions to fire. If given, action_name must be a part of it for this function to return true.

:exclude<Array>

Optional list of actions not to fire. If given, action_name must not be a part of it for this function to return true.

Returns

Boolean

True if the action should be called.

:api: private

Returns:

  • (Boolean)

379
380
381
382
383
384
385
# File 'lib/merb-core/controller/abstract_controller.rb', line 379

def _call_filter_for_action?(rule, action_name)
  # Both:
  # * no :only or the current action is in the :only list
  # * no :exclude or the current action is not in the :exclude list
  (!rule.key?(:only) || rule[:only].include?(action_name)) &&
  (!rule.key?(:exclude) || !rule[:exclude].include?(action_name))
end

#_dispatch(action) ⇒ Object

This will dispatch the request, calling internal before/after dispatch callbacks.

If the return value of _call_filters is not :filter_chain_completed the action is not called, and the return from the filters is used instead.

Parameters

action<~to_s>

The action to dispatch to. This will be #send'ed in _call_action. Defaults to :to_s.

Returns

<~to_s>

Returns the string that was returned from the action.

Raises

ArgumentError

Invalid result caught from before filters.

:api: plugin


280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
# File 'lib/merb-core/controller/abstract_controller.rb', line 280

def _dispatch(action)
  self.action_name = action
  self._before_dispatch_callbacks.each { |cb| cb.call(self) }

  caught = catch(:halt) do
    start = Time.now
    result = _call_filters(_before_filters)
    @_benchmarks[:before_filters_time] = Time.now - start if _before_filters

    @body = _call_action(action_name) if result == :filter_chain_completed

    result
  end
  
  @body = case caught
  when :filter_chain_completed  then @body
  when String                   then caught
  # return *something* if you throw halt with nothing
  when nil                      then "<html><body><h1>Filter Chain Halted!</h1></body></html>"
  when Symbol                   then __send__(caught)
  when Proc                     then self.instance_eval(&caught)
  else
    raise ArgumentError, "Threw :halt, #{caught}. Expected String, nil, Symbol, Proc."
  end
  start = Time.now
  _call_filters(_after_filters)
  @_benchmarks[:after_filters_time] = Time.now - start if _after_filters

  self._after_dispatch_callbacks.each { |cb| cb.call(self) }

  @body
end

#_evaluate_condition(condition) ⇒ Object

Evaluates a filter condition (:if or :unless)

Parameters

condition<Symbol, Proc>

The condition to evaluate.

Raises

ArgumentError

condition not a Symbol or Proc.

Returns

Boolean

True if the condition is met.

Alternatives

If condition is a symbol, it will be send'ed. If it is a Proc it will be called directly with self as an argument.

:api: private


425
426
427
428
429
430
431
432
433
# File 'lib/merb-core/controller/abstract_controller.rb', line 425

def _evaluate_condition(condition)
  case condition
  when Symbol then self.send(condition)
  when Proc then self.instance_eval(&condition)
  else
    raise ArgumentError,
          'Filter condtions need to be either a Symbol or a Proc'
  end
end

#_filter_condition_met?(rule) ⇒ Boolean

Determines whether the filter should be run based on the conditions passed (:if and :unless)

Parameters

rule<Hash>

Rules for the filter (see below).

Options (rule)

:if<Array>

Optional conditions that must be met for the filter to fire.

:unless<Array>

Optional conditions that must not be met for the filter to fire.

Returns

Boolean

True if the conditions are met.

:api: private

Returns:

  • (Boolean)

401
402
403
404
405
406
407
# File 'lib/merb-core/controller/abstract_controller.rb', line 401

def _filter_condition_met?(rule)
  # Both:
  # * no :if or the if condition evaluates to true
  # * no :unless or the unless condition evaluates to false
  (!rule.key?(:if) || _evaluate_condition(rule[:if])) &&
  (!rule.key?(:unless) || ! _evaluate_condition(rule[:unless]))
end

#_template_location(context, type, controller) ⇒ Object

This is called after the controller is instantiated to figure out where to look for templates under the _template_root. Override this to define a new structure for your app.

Parameters

context<~to_s>

The controller context (the action or template name).

type<~to_s>

The content type. Could be nil.

controller<~to_s>

The name of the controller. Defaults to being called with the controller_name. Set t

Returns

String

Indicating where to look for the template for the current controller, context, and content-type.

Notes

The type is irrelevant for controller-types that don't support content-type negotiation, so we default to not include it in the superclass.

Examples

def _template_location
  "#{params[:controller]}.#{params[:action]}.#{content_type}"
end

This would look for templates at controller.action.mime.type instead of controller/action.mime.type

:api: public


171
172
173
# File 'lib/merb-core/controller/abstract_controller.rb', line 171

def _template_location(context, type, controller)
  controller ? "#{controller}/#{context}" : context
end

#absolute_url(*args) ⇒ Object

Returns the absolute url including the passed protocol and host.

This uses the same arguments as the url method, with added requirements of protocol and host options.

:api: public

Raises:

  • (ArgumentError)

557
558
559
560
561
562
563
564
565
566
567
568
569
570
# File 'lib/merb-core/controller/abstract_controller.rb', line 557

def absolute_url(*args)
  # FIXME: arrgh, why request.protocol returns http://?
  # :// is not part of protocol name
  options  = extract_options_from_args!(args) || {}
  protocol = options.delete(:protocol)
  host     = options.delete(:host)

  raise ArgumentError, "The :protocol option must be specified" unless protocol
  raise ArgumentError, "The :host option must be specified"     unless host

  args << options

  protocol + "://" + host + url(*args)
end

#capture(*args, &block) ⇒ Object

Calls the capture method for the selected template engine.

Parameters

*args

Arguments to pass to the block.

&block

The block to call.

Returns

String

The output of a template block or the return value of a non-template block converted to a string.

:api: public


616
617
618
619
620
621
622
623
624
625
# File 'lib/merb-core/controller/abstract_controller.rb', line 616

def capture(*args, &block)
  ret = nil

  captured = send("capture_#{@_engine}", *args) do |*args|
    ret = yield *args
  end

  # return captured value only if it is not empty
  captured.empty? ? ret.to_s : captured
end

#concat(str, binding) ⇒ Object

Calls the concatenate method for the selected template engine.

Parameters

str<String>

The string to concatenate to the buffer.

binding<Binding>

The binding to use for the buffer.

:api: public


634
635
636
# File 'lib/merb-core/controller/abstract_controller.rb', line 634

def concat(str, binding)
  send("concat_#{@_engine}", str, binding)
end

#controller_nameObject

Returns

String

The controller name in path form, e.g. “admin/items”.

:api: public


138
# File 'lib/merb-core/controller/abstract_controller.rb', line 138

def controller_name()      self.class.controller_name                   end

#resource(*args) ⇒ Object

Generates a URL for a single or nested resource.

Parameters

resources<Symbol,Object>

The resources for which the URL

should be generated. These resources should be specified
in the router.rb file using #resources and #resource.
options<Hash>

Any extra parameters that are needed to

generate the URL.

Returns

String

The generated URL.

Examples

Merb::Router.prepare do

resources :users do
  resources :comments
end

end

resource(:users) # => /users resource(@user) # => /users/10 resource(@user, :comments) # => /users/10/comments resource(@user, @comment) # => /users/10/comments/15 resource(:users, :new) # => /users/new resource(:@user, :edit) # => /users/10/edit

:api: public


601
602
603
604
# File 'lib/merb-core/controller/abstract_controller.rb', line 601

def resource(*args)
  args << {}
  Merb::Router.resource(*args)
end

#url(name, *args) ⇒ Object Also known as: relative_url

There are three possible ways to use this method. First, if you have a named route, you can specify the route as the first parameter as a symbol and any paramters in a hash. Second, you can generate the default route by just passing the params hash, just passing the params hash. Finally, you can use the anonymous parameters. This allows you to specify the parameters to a named route in the order they appear in the router.

Parameters(Named Route)

name<Symbol>

The name of the route.

args<Hash>

Parameters for the route generation.

Parameters(Default Route)

args<Hash>

Parameters for the route generation. This route will use the default route.

Parameters(Anonymous Parameters)

name<Symbol>

The name of the route.

args<Array>

An array of anonymous parameters to generate the route with. These parameters are assigned to the route parameters in the order that they are passed.

Returns

String

The generated URL.

Examples

Named Route

Merb::Router.prepare do

match("/articles/:title").to(:controller => :articles, :action => :show).name("articles")

end

url(:articles, :title => “new_article”)

Default Route

Merb::Router.prepare do

default_routes

end

url(:controller => “articles”, :action => “new”)

Anonymous Paramters

Merb::Router.prepare do

match("/articles/:year/:month/:title").to(:controller => :articles, :action => :show).name("articles")

end

url(:articles, 2008, 10, “test_article”)

:api: public


544
545
546
547
# File 'lib/merb-core/controller/abstract_controller.rb', line 544

def url(name, *args)
  args << {}
  Merb::Router.url(name, *args)
end