Class: Ramaze::Controller
- Includes:
- Helper::Methods
- Defined in:
- lib/ramaze/controller.rb,
lib/ramaze/controller/error.rb,
lib/ramaze/controller/resolve.rb,
lib/ramaze/contrib/auto_params.rb
Overview
The Controller is responsible for combining and rendering actions.
Direct Known Subclasses
Constant Summary collapse
- FILTER =
TODO:
* fix caching, see todos below FILTER = [ :cached, :default ] unless defined?(FILTER)
[ :default ]
Class Method Summary collapse
-
.action_methods ⇒ Object
methodnames that may be used for current controller.
-
.at(mapping) ⇒ Object
Returns the Controller at a mapped path.
-
.cached(path) ⇒ Object
ignore cache when request.params is interesting.
-
.cached_action_methods ⇒ Object
List or create a list of action methods to be cached.
-
.check_path(message, *paths) ⇒ Object
checks paths for existance and logs a warning if it doesn’t exist yet.
-
.current ⇒ Object
Return Controller of current Action.
-
.default(path) ⇒ Object
Default element of FILTER.
-
.deny_layout(*actions) ⇒ Object
Deny layout for passed names of actions.
-
.engine(name) ⇒ Object
This is a method to specify the templating engine for your controller.
-
.extension_order ⇒ Object
Uses custom defined engines and all available engines and throws it against the extensions for the template to find the most likely templating-engine to use ordered by priority and likelyhood.
-
.handle(path) ⇒ Object
Entering point for Dispatcher, first Controller::resolve(path) and then renders the resulting Action.
-
.inherited(controller) ⇒ Object
When Controller is subclassed the resulting class is placed in Global.controllers and a new trait :actions_cached is set on it.
-
.layout(*meth_or_hash) ⇒ Object
Define a layout for all actions on this controller.
-
.map(*syms) ⇒ Object
Map Controller to the given syms or strings.
-
.mapping ⇒ Object
if trait is set and controller is not in Global.mapping yet this will build a new default mapping-point, MainController is put at ‘/’ by default.
-
.pattern_for(path) ⇒ Object
Generate all possible permutations for given path.
-
.raise_no_action(controller, path) ⇒ Object
Raises Ramaze::Error::NoAction.
-
.raise_no_controller(path) ⇒ Object
Raises Ramaze::Error::NoController.
-
.raise_no_filter(path) ⇒ Object
Raises Ramaze::Error::NoFilter TODO: * is this called at all for anybody? I think everybody does have filters.
- .relevant_ancestors(parent = Ramaze::Controller) ⇒ Object
-
.resolve(path, routed = false) ⇒ Object
Resolve an absolute path in the application by passing it to each element of Ramaze::Controller::FILTER.
-
.resolve_action(path, *parameter) ⇒ Object
Try to produce an Action from the given path and paremters with the appropiate template if one exists.
-
.resolve_method(name, *params) ⇒ Object
use Method#get_args to insert values from request.params into Action#params.
-
.resolve_template(path) ⇒ Object
Search the #template_paths for a fitting template for path.
-
.startup(options = {}) ⇒ Object
called from Ramaze.startup, adds Cache.actions and Cache.patterns, walks all controllers subclassed so far and adds them to the Global.mapping if they are not assigned yet.
-
.template(this, *argv) ⇒ Object
This is used for template rerouting, takes action, optionally a controller and action to route to.
-
.template_paths ⇒ Object
Composes an array with the template-paths to look up in the right order.
- .template_root(*args) ⇒ Object
-
.view_root(*args) ⇒ Object
Define a view_root for Controller, returns the current view_root if no argument is given.
Instance Method Summary collapse
-
#error ⇒ Object
The default error-page handler.
Methods included from Helper::Methods
extend_object, #helper, included
Class Method Details
.action_methods ⇒ Object
methodnames that may be used for current controller.
174 175 176 177 178 179 180 181 182 |
# File 'lib/ramaze/controller/resolve.rb', line 174 def action_methods ancs = relevant_ancestors + Helper::LOOKUP.to_a ancs.reverse.inject [] do |meths, anc| meths + anc.public_instance_methods(false).map{|im| im.to_s } - anc.private_instance_methods(false).map{|im| im.to_s } end end |
.at(mapping) ⇒ Object
Returns the Controller at a mapped path.
95 96 97 |
# File 'lib/ramaze/controller.rb', line 95 def at(mapping) Global.mapping[mapping.to_s] end |
.cached(path) ⇒ Object
ignore cache when request.params is interesting
36 37 38 39 40 41 42 43 44 45 46 47 |
# File 'lib/ramaze/controller/resolve.rb', line 36 def cached(path) if found = Cache.resolved[path] if found.respond_to?(:relaxed_hash) return found.dup else Log.warn("Found faulty `#{path}' in Cache.resolved, deleting it for sanity.") Cache.resolved.delete path end end nil end |
.cached_action_methods ⇒ Object
List or create a list of action methods to be cached
169 170 171 |
# File 'lib/ramaze/controller/resolve.rb', line 169 def cached_action_methods Cache.action_methods[self] ||= action_methods end |
.check_path(message, *paths) ⇒ Object
checks paths for existance and logs a warning if it doesn’t exist yet.
62 63 64 65 66 |
# File 'lib/ramaze/controller.rb', line 62 def check_path(, *paths) paths.each do |path| Log.warn( % path) unless File.directory?(path) end end |
.current ⇒ Object
Return Controller of current Action
252 253 254 255 |
# File 'lib/ramaze/controller.rb', line 252 def current action = Action.current action.instance || action.controller end |
.default(path) ⇒ Object
Default element of FILTER. The default handler that tries to find the best match for the given path in terms of Controller/method/template and given arguments. If a match is found it will be cached for further use.
54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 |
# File 'lib/ramaze/controller/resolve.rb', line 54 def default(path) mapping = Global.mapping controllers = Global.controllers raise_no_controller(path) if controllers.empty? or mapping.empty? # TODO: # * following code is dangerous (DoS): # patterns = Cache.patterns[path] ||= pattern_for(path) patterns = pattern_for(path) first_controller = nil patterns.each do |controller, method, params| if controller = mapping[controller] first_controller ||= controller action = controller.resolve_action(method, *params) next unless action template = action.template valid_action = if Action.stack.size > 0 || Global.actionless_templates action.method or (params.empty? && template) else action.method end if valid_action # TODO: # * dangerous as well # Cache.resolved[path] = action return action end end end if !@routed and new_path = Route.resolve(path) Log.dev("Routing from `#{path}' to `#{new_path}'") return resolve(new_path, true) end raise_no_action(first_controller, path) if first_controller raise_no_controller(path) end |
.deny_layout(*actions) ⇒ Object
Deny layout for passed names of actions. Name should be name of the method or template without extension, as String or Symbol
Usage:
class MainController < Ramaze::Controller
deny_layout :atom
def atom
"I won't have layout"
end
end
142 143 144 145 146 |
# File 'lib/ramaze/controller.rb', line 142 def deny_layout(*actions) actions.each do |action| layout[:deny] << action.to_s end end |
.engine(name) ⇒ Object
This is a method to specify the templating engine for your controller. It basically just is sugar for:
trait :engine => Haml
Usage:
class MainController < Ramaze::Controller
engine :Haml
end
241 242 243 244 245 246 247 248 |
# File 'lib/ramaze/controller.rb', line 241 def engine(name) name = Template.const_get(name) rescue NameError => ex Log.warn ex Log.warn "Try to use passed engine directly" ensure trait :engine => name end |
.extension_order ⇒ Object
Uses custom defined engines and all available engines and throws it against the extensions for the template to find the most likely templating-engine to use ordered by priority and likelyhood.
217 218 219 220 221 222 223 224 225 226 227 |
# File 'lib/ramaze/controller/resolve.rb', line 217 def extension_order t_extensions = Template::ENGINES all_extensions = t_extensions.values.flatten if engine = trait[:engine] c_extensions = t_extensions.select{|k,v| k == engine}.map{|k,v| v}.flatten return (c_extensions + all_extensions).uniq end all_extensions end |
.handle(path) ⇒ Object
Entering point for Dispatcher, first Controller::resolve(path) and then renders the resulting Action.
260 261 262 263 264 |
# File 'lib/ramaze/controller.rb', line 260 def handle path action = resolve(path) Thread.current[:controller] = action.controller action.render end |
.inherited(controller) ⇒ Object
When Controller is subclassed the resulting class is placed in Global.controllers and a new trait :actions_cached is set on it.
36 37 38 39 40 41 42 43 |
# File 'lib/ramaze/controller.rb', line 36 def inherited controller controller.trait :actions_cached => {} Global.controllers << controller if map = controller.mapping Log.dev("mapping #{map} => #{controller}") Global.mapping[map] ||= controller end end |
.layout(*meth_or_hash) ⇒ Object
Define a layout for all actions on this controller
Example:
class Foo < Ramaze::Controller
layout :foo
end
This defines the action :foo to be layout of the controller and will
render the layout after any other action has been rendered, assigns
@content to the result of the action and then goes on rendering
the layout-action where @content may or may not be used, returning
whatever the layout returns.
112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 |
# File 'lib/ramaze/controller.rb', line 112 def layout(*meth_or_hash) if meth_or_hash.empty? trait[:layout] ||= ( ancestral_trait[:layout] || {:all => nil, :deny => Set.new} ).dup else meth_or_hash = meth_or_hash.first if meth_or_hash.respond_to?(:to_hash) meth_or_hash.each do |layout_name, *actions| actions.flatten.each do |action| layout[action.to_s] = layout_name end end else layout[:all] = meth_or_hash end end end |
.map(*syms) ⇒ Object
Map Controller to the given syms or strings. Replaces old mappings. If you want to add a mapping, just modify Global.mapping.
85 86 87 88 89 90 91 |
# File 'lib/ramaze/controller.rb', line 85 def map(*syms) Global.mapping.delete_if{|k,v| v == self} syms.each do |sym| Global.mapping[sym.to_s] = self end end |
.mapping ⇒ Object
if trait is set and controller is not in Global.mapping yet this will build a new default mapping-point, MainController is put at ‘/’ by default.
72 73 74 75 76 77 78 79 |
# File 'lib/ramaze/controller.rb', line 72 def mapping global_mapping = Global.mapping.invert[self] return global_mapping if global_mapping if ancestral_trait[:automap] && self.to_s !~ /#<Class:/ name = self.to_s.gsub('Controller', '').gsub('::', '/').clone name == 'Main' ? '/' : "/#{name.snake_case}" end end |
.pattern_for(path) ⇒ Object
Generate all possible permutations for given path.
185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 |
# File 'lib/ramaze/controller/resolve.rb', line 185 def pattern_for(path) atoms = path.to_s.split('/').grep(/\S/) atoms.unshift('') patterns, joiner = [], '/' atoms.size.times do |enum| enum += 1 pattern = atoms.dup controller = pattern[0, enum].join(joiner) controller.gsub!(/^__/, '/') controller = "/" if controller == "" pattern = pattern[enum..-1] args, temp = [], [] patterns << [controller, 'index', atoms[enum..-1]] until pattern.empty? args << pattern.shift joined = args.join('__') patterns << [controller, joined, pattern.dup] patterns << [controller, "#{joined}__index", pattern.dup] end end patterns.reverse! end |
.raise_no_action(controller, path) ⇒ Object
Raises Ramaze::Error::NoAction
246 247 248 249 |
# File 'lib/ramaze/controller/resolve.rb', line 246 def raise_no_action(controller, path) Thread.current[:controller] = controller raise Ramaze::Error::NoAction, "No Action found for `#{path}' on #{controller}" end |
.raise_no_controller(path) ⇒ Object
Raises Ramaze::Error::NoController
240 241 242 |
# File 'lib/ramaze/controller/resolve.rb', line 240 def raise_no_controller(path) raise Ramaze::Error::NoController, "No Controller found for `#{path}'" end |
.raise_no_filter(path) ⇒ Object
Raises Ramaze::Error::NoFilter TODO:
* is this called at all for anybody?
I think everybody does have filters.
234 235 236 |
# File 'lib/ramaze/controller/resolve.rb', line 234 def raise_no_filter(path) raise Ramaze::Error::NoFilter, "No Filter found for `#{path}'" end |
.relevant_ancestors(parent = Ramaze::Controller) ⇒ Object
266 267 268 269 270 |
# File 'lib/ramaze/controller.rb', line 266 def relevant_ancestors(parent = Ramaze::Controller) ancestors.select do |anc| anc.ancestors.include?(parent) end end |
.resolve(path, routed = false) ⇒ Object
Resolve an absolute path in the application by passing it to each element of Ramaze::Controller::FILTER. If an element does not respond to call it will be sent to self instead, in either case with path as argument.
18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
# File 'lib/ramaze/controller/resolve.rb', line 18 def resolve(path, routed = false) @routed = routed FILTER.each do |filter| answer = if filter.respond_to?(:call) filter.call(path) else send(filter.to_s, path) end return answer if answer end raise_no_filter(path) end |
.resolve_action(path, *parameter) ⇒ Object
Try to produce an Action from the given path and paremters with the appropiate template if one exists.
103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 |
# File 'lib/ramaze/controller/resolve.rb', line 103 def resolve_action(path, *parameter) path, parameter = path.to_s, parameter.map{|e| e.to_s} if info = trait["#{path}_template"] template = info[:file] unless template controller, action = info.values_at :controller, :action template = controller.resolve_template(action) end end method, params = resolve_method(path, *parameter) if method or parameter.empty? template ||= resolve_template(path) end action = Action.create :path => path, :method => method, :params => params, :template => template, :controller => self return false unless action.valid_rest? action end |
.resolve_method(name, *params) ⇒ Object
use Method#get_args to insert values from request.params into Action#params
156 157 158 159 160 161 162 163 164 165 |
# File 'lib/ramaze/controller/resolve.rb', line 156 def resolve_method(name, *params) if method = [ name, name.gsub('__','/') ].find{|n| cached_action_methods.include?(n) } arity = instance_method(method).arity if params.size == arity or arity < 0 return method, params end end return nil, [] end |
.resolve_template(path) ⇒ Object
Search the #template_paths for a fitting template for path. Only the first found possibility for the generated glob is returned.
132 133 134 135 136 137 138 139 140 141 |
# File 'lib/ramaze/controller/resolve.rb', line 132 def resolve_template(path) path = path.to_s path_converted = path.split('__').inject{|s,v| s/v} possible_paths = [path, path_converted].compact paths = template_paths.map{|pa| possible_paths.map{|a| pa/a } }.flatten.uniq glob = "{#{paths.join(',')}}.{#{extension_order.join(',')}}" Dir[glob].first end |
.startup(options = {}) ⇒ Object
called from Ramaze.startup, adds Cache.actions and Cache.patterns, walks all controllers subclassed so far and adds them to the Global.mapping if they are not assigned yet.
49 50 51 52 53 54 55 56 57 58 |
# File 'lib/ramaze/controller.rb', line 49 def startup = {} Log.dev("found Controllers: #{Global.controllers.inspect}") check_path("Public root: '%s' doesn't exist", Global.public_root) check_path("View root: '%s' doesn't exist", Global.view_root) require 'ramaze/controller/main' if Global.mapping.empty? Log.debug("mapped Controllers: #{Global.mapping.inspect}") end |
.template(this, *argv) ⇒ Object
This is used for template rerouting, takes action, optionally a controller and action to route to.
Usage:
class MainController
template :index, OtherController, :list
template :foo, :bar
template :bar, :file => '/absolute/path'
template :baz, :file => 'relative/path'
template :abc, :controller => OtherController
template :xyz, :controller => OtherController, :action => 'list'
def index
'will use template from OtherController#list'
end
def foo
'will use template from self#bar'
end
def
'will use template from /absolute/path'
end
def baz
'will use template from relative/path'
end
def abc
'will use template from OtherController#index'
end
def xyz
'will use template from OtherController#list'
end
end
203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 |
# File 'lib/ramaze/controller.rb', line 203 def template(this, *argv) case argv.first when Hash , *ignored = argv controller = [:controller] || ['controller'] action = [:action] || ['action'] file = [:file] || ['file'] info = {} if file file = file.to_s unless Pathname(file).absolute? root = [view_root || Global.view_root].flatten.first file = File.join(root, file) end info[:file] = file else controller ||= self action = (action || 'index').to_s info[:controller] = controller info[:action] = action end trait "#{this}_template" => info else controller, action, *ignored = argv controller, action = self, controller unless action trait "#{this}_template" => {:controller => controller, :action => action} end end |
.template_paths ⇒ Object
Composes an array with the template-paths to look up in the right order. Usually this is composed of Global.view_root and the mapping of the controller.
147 148 149 150 151 152 153 |
# File 'lib/ramaze/controller/resolve.rb', line 147 def template_paths if paths = view_root paths else view_root(Global.view_root / Global.mapping.invert[self]) end end |
.template_root(*args) ⇒ Object
161 162 163 164 |
# File 'lib/ramaze/controller.rb', line 161 def template_root(*args) Ramaze.deprecated("Controller::template_root", "Controller::view_root") view_root(*args) end |
.view_root(*args) ⇒ Object
Define a view_root for Controller, returns the current view_root if no argument is given. Runs every given path through Controller::check_path
152 153 154 155 156 157 158 159 |
# File 'lib/ramaze/controller.rb', line 152 def view_root *args if args.empty? @view_root else check_path("#{self}.view_root: '%s' doesn't exist", *args) @view_root = args.flatten end end |
Instance Method Details
#error ⇒ Object
The default error-page handler. you can overwrite this method in your controller and create your own error-template for use.
Error-pages can be in whatever the templating-engine of your controller is set to.
Ramaze::Dispatcher::Error.current
holds the exception thrown.
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
# File 'lib/ramaze/controller/error.rb', line 14 def error error = Dispatcher::Error.current title = error. unless Action.current.template response['Content-Type'] = 'text/plain' respond %( #{error.} #{error.backtrace.join("\n ")} #{PP.pp request, '', 200} ).ui end backtrace_size = Global.backtrace_size @backtrace = error.backtrace[0..20].map{|line| file, lineno, meth = *Ramaze.parse_backtrace(line) lines = Ramaze.caller_lines(file, lineno, backtrace_size) [ lines, lines.object_id.abs, file, lineno, meth ] } # for backwards-compat with old error.zmr @colors = [255] * @backtrace.size @title = h(title) @editor = ENV['EDITOR'] || 'vim' title rescue Object => ex Log.error(ex) end |