| 63 | | |
|---|
| 64 | | function callAction(handler, req, res, session) { |
|---|
| 65 | | res.push(); |
|---|
| 66 | | // try { |
|---|
| 67 | | handler.call(this); |
|---|
| 68 | | /* } catch(e) { |
|---|
| 69 | | res.write(e.rhinoException); |
|---|
| 70 | | } */ |
|---|
| 71 | | if (!res.calledRender) this.render(); |
|---|
| 72 | | content = res.pop(); |
|---|
| 73 | | return content; |
|---|
| 74 | | } |
|---|
| 75 | | |
|---|
| 76 | | /** |
|---|
| 77 | | * Returns the handler function (action), that matches the given route. |
|---|
| 78 | | * This function will be called by routing.handleRequest. |
|---|
| 79 | | * <p>When a controller object processes a request, it looks for the corresponding instance method |
|---|
| 80 | | * for the incoming action. The method can be defined as a property of the actions object within the |
|---|
| 81 | | * controller, or as a method, which name ends with _action. It will look up the method in the following |
|---|
| 82 | | * order. |
|---|
| 83 | | * <ol> |
|---|
| 84 | | * <li> actions.{format}.{method}.{action} |
|---|
| 85 | | * <li> actions.{format}.{action} |
|---|
| 86 | | * <li> actions.{action} |
|---|
| 87 | | * <li> {action}_{method}_{format}_action |
|---|
| 88 | | * <li> {action}_{action}_action |
|---|
| 89 | | * <li> {action}_{method}_action |
|---|
| 90 | | * <li> {action}_action |
|---|
| 91 | | * </ol> |
|---|
| 92 | | * <p>If it finds one, that method is invoked. If no method can be called, the controller looks for |
|---|
| 93 | | * a template named after the current controller and action. If found, this template is rendered directly. |
|---|
| 94 | | * If not, it will try to find a corresponding method for the action “notfound”, and that method is called. |
|---|
| 95 | | * Because aida.controller, which is the parent of all Action Controllers defines a “notfound_action”, |
|---|
| 96 | | * the controller always returns a result. |
|---|
| 97 | | * <p>In contrast to Rails, not all public methods in a controller may be invoked as an action method. |
|---|
| 98 | | * Instead you have to add “_action” to the method name, or make it a property of the actions object within |
|---|
| 99 | | * the controller. Because of this fact there is no need for a method “hide_action”. |
|---|
| 100 | | * <pre class=javascript> |
|---|
| 101 | | * importModule(“aida.controller”, “Controller”); |
|---|
| 102 | | * this.__proto__ = Controller; |
|---|
| 103 | | * |
|---|
| 104 | | * // will handle /controller/index |
|---|
| 105 | | * function index_action() { |
|---|
| 106 | | * render(); |
|---|
| 107 | | * } |
|---|
| 108 | | * |
|---|
| 109 | | * // will handle POST /controller/index |
|---|
| 110 | | * function index_post_action() { |
|---|
| 111 | | * // handle post data |
|---|
| 112 | | * res.redirect("/" + this.getShortName()); // back to index |
|---|
| 113 | | * } |
|---|
| 114 | | * |
|---|
| 115 | | * // will handle /controller/index.rss |
|---|
| 116 | | * function index_rss_action() { |
|---|
| 117 | | * // print this content as rss |
|---|
| 118 | | * } |
|---|
| 119 | | * |
|---|
| 120 | | * this.actions.json = { |
|---|
| 121 | | * // will handle /controller/index.json |
|---|
| 122 | | * index : function() { |
|---|
| 123 | | * // index as json |
|---|
| 124 | | * }, |
|---|
| 125 | | * // will handle POST /controller/index.json |
|---|
| 126 | | * index.post : function() { |
|---|
| 127 | | * // handle post data (submited as json) |
|---|
| 128 | | * } |
|---|
| 129 | | * } |
|---|
| 130 | | * |
|---|
| 131 | | * </pre> |
|---|
| 132 | | * |
|---|
| 133 | | * |
|---|
| 134 | | * @param {object} [route] If route isn't passed to the function, we will fallback to req.route |
|---|
| 135 | | * @config {string} action Action name, that should be called |
|---|
| 136 | | * @config {string} [format] Format name, matching an incoming format (xml, html, json, ...) |
|---|
| 137 | | * @config {string} [method] HTTP-Moethod (post, get, delete, ...) |
|---|
| 138 | | * @return {function} Returns the matching handler function |
|---|
| 139 | | * @see routing.handleRequest |
|---|
| 140 | | */ |
|---|
| 141 | | function getAction(route) { |
|---|
| 142 | | var h; |
|---|
| 143 | | var route = route || req.route; |
|---|
| 144 | | logger.info("route.action:" + route.action); |
|---|
| 145 | | var action = (route.action || "").toLowerCase().replace(".", "_").underscore(); |
|---|
| 146 | | var method = (route.method || "").toLowerCase(); |
|---|
| 147 | | var format = (route.format || "").toLowerCase(); |
|---|
| 148 | | |
|---|
| 149 | | h = ( |
|---|
| 150 | | this.actions && this.actions[format] && this.actions[format][method] && this.actions[format][method][action] || |
|---|
| 151 | | this.actions && this.actions[format] && this.actions[format][action] || |
|---|
| 152 | | this.actions && this.actions[action] |
|---|
| 153 | | ) |
|---|
| 154 | | if (!h) h = ( |
|---|
| 155 | | this[action + "_" + method + "_" + format + "_action"] || |
|---|
| 156 | | this[action + "_" + method + "_action"] || |
|---|
| 157 | | this[action + "_" + format + "_action"] || |
|---|
| 158 | | this[action + "_action"] |
|---|
| 159 | | ) |
|---|
| 160 | | if (!h && this.getTemplateSource().exists) { |
|---|
| 161 | | h = function() { |
|---|
| 162 | | render.call(this); |
|---|
| 163 | | } |
|---|
| 164 | | } |
|---|
| 165 | | if (!h) h = ( |
|---|
| 166 | | (action != "notfound") ? this.getAction({action:"notfound", format:format, method:method}) : null |
|---|
| 167 | | ) |
|---|
| 168 | | return h; |
|---|
| 169 | | } |
|---|
| 170 | | |
|---|
| 171 | | /** |
|---|
| 172 | | * Writes content to the response. |
|---|
| 173 | | * |
|---|
| 174 | | * <p>Depending on the options you pass to the function it will do the following |
|---|
| 175 | | * |
|---|
| 176 | | * <dl> |
|---|
| 177 | | * <dt>render()</dt><dd> |
|---|
| 178 | | * With no parameter, the render method renders the default template |
|---|
| 179 | | * for the current controller and action. The following code will render the |
|---|
| 180 | | * template <span class=path>app/views/blog/index.skin</span> |
|---|
| 181 | | * <pre class=javascript> |
|---|
| 182 | | * importModule(“aida.controller”, “Controller”); |
|---|
| 183 | | * this.__proto__ = Controller; |
|---|
| 184 | | * |
|---|
| 185 | | * function index() { |
|---|
| 186 | | * render(); |
|---|
| 187 | | * } |
|---|
| 188 | | * </pre> |
|---|
| 189 | | * |
|---|
| 190 | | * <p>render() may just be called once per request. If you don't call render() in your |
|---|
| 191 | | * action method, Controller, or to be more specific - handleRequest, will |
|---|
| 192 | | * perform it without any parameters. |
|---|
| 193 | | * <p>If you don't specify a handler method for the action, Action Controller will try |
|---|
| 194 | | * to find the corresponding template (skin) and call it. If it can't find a method nor |
|---|
| 195 | | * a template for the matching route (controller/action) it will call the action "notfound" |
|---|
| 196 | | * for the controller. |
|---|
| 197 | | * </dd> |
|---|
| 198 | | * |
|---|
| 199 | | * <dt>render({text:<string>})</dt><dd> |
|---|
| 200 | | * <p>Sends the given string to the response. |
|---|
| 201 | | * No template interpretation or HTML escaping is performed. |
|---|
| 202 | | * <pre class=javascript> |
|---|
| 203 | | * function index_action() { |
|---|
| 204 | | * render({text: "Hello there!"}); |
|---|
| 205 | | * } |
|---|
| 206 | | * </pre> |
|---|
| 207 | | * </dd> |
|---|
| 208 | | * |
|---|
| 209 | | * <dt>render({template:<string>, type:<skin|extension>, context:<object>})</dt><dd> |
|---|
| 210 | | * <p>Interprets string as the source to a template of the given type, rendering the |
|---|
| 211 | | * results back to the client. If the :locals hash is given, the contents are used |
|---|
| 212 | | * to set the values of local variables in the template. |
|---|
| 213 | | * <p>The following code adds method_missing to a controller if the application is |
|---|
| 214 | | * running in development mode. If the controller is called with an invalid action, |
|---|
| 215 | | * this renders an inline template to display the action’s name and a formatted version |
|---|
| 216 | | * of the request parameters. |
|---|
| 217 | | * <code class=javascript> |
|---|
| 218 | | * function welcome_action() { |
|---|
| 219 | | * render({ |
|---|
| 220 | | * template: |
|---|
| 221 | | * '<h2>Hello <% name %>!</h2> \ |
|---|
| 222 | | * <p>Welcome, and have a nice day!</p>', |
|---|
| 223 | | * context : { name : "Matthias" } |
|---|
| 224 | | * }); |
|---|
| 225 | | * } |
|---|
| 226 | | * </code> |
|---|
| 227 | | * <p>Note: You have to end each line of the multi line string with a backslash, |
|---|
| 228 | | * otherwise you will get an “unterminated string literal” error from Rhino. |
|---|
| 229 | | * </dd> |
|---|
| 230 | | * |
|---|
| 231 | | * <dt>render({action:<string>})</dt><dd> |
|---|
| 232 | | * <p>Renders the template for a given action in this controller. |
|---|
| 233 | | * <pre class=javascript> |
|---|
| 234 | | * function display_cart_action() { |
|---|
| 235 | | * if (cart.isEmpty()) { |
|---|
| 236 | | * render({action:"index"}); |
|---|
| 237 | | * } else { |
|---|
| 238 | | * // ... |
|---|
| 239 | | * } |
|---|
| 240 | | * } |
|---|
| 241 | | * </pre> |
|---|
| 242 | | * Note that calling render({action:...}) does not call the action method; |
|---|
| 243 | | * it simply displays the template. If the template needs instance variables, |
|---|
| 244 | | * these must be set up by the method that calls the render. |
|---|
| 245 | | * </dd> |
|---|
| 246 | | * </dl> |
|---|
| 247 | | * |
|---|
| 248 | | * @param {object} options Options for defining the output. See description. |
|---|
| 249 | | */ |
|---|
| 250 | | function render(options) { |
|---|
| 251 | | if (!res.contentType) res.contentType = routing.Formats.getMimeType(req.route.format); |
|---|
| 252 | | if (!options) options = {}; |
|---|
| 253 | | if (res.calledRender) throw new DoubleRenderError(); |
|---|
| 254 | | res.calledRender = true; |
|---|
| 255 | | var action = options.action || req.route.action; |
|---|
| 256 | | var context = {}; |
|---|
| 257 | | for (var name in this.helpers) { |
|---|
| 258 | | if (this.helpers[name]._namespace) { |
|---|
| 259 | | context[this.helpers[name]._namespace] = this.helpers[name]; |
|---|
| 260 | | } else { |
|---|
| 261 | | context = Object.extend(context, this.helpers[name]); |
|---|
| 262 | | } |
|---|
| 263 | | } |
|---|
| 264 | | context = Object.extend( |
|---|
| 265 | | context, |
|---|
| 266 | | { |
|---|
| 267 | | request : req.data, |
|---|
| 268 | | controller : this, |
|---|
| 269 | | flash : req.flash, |
|---|
| 270 | | headers : req.headers, |
|---|
| 271 | | logger : logger, |
|---|
| 272 | | params : req.data, |
|---|
| 273 | | request : req, |
|---|
| 274 | | response : res, |
|---|
| 275 | | session : session |
|---|
| 276 | | }, |
|---|
| 277 | | this.context || {} |
|---|
| 278 | | ); |
|---|
| 279 | | if (options.context) { |
|---|
| 280 | | context = Object.extend(context, options.context); |
|---|
| 281 | | } |
|---|
| 282 | | |
|---|
| 283 | | if (typeof options === "string" && options != "") { |
|---|
| 284 | | res.writeln(options); |
|---|
| 285 | | } else if (options && options.inline) { |
|---|
| 286 | | var options = Object.extend({ |
|---|
| 287 | | type : "skin", |
|---|
| 288 | | locals : {} |
|---|
| 289 | | }, options); |
|---|
| 290 | | if (options.type === "skin") { |
|---|
| 291 | | /* |
|---|
| 292 | | var skin = []; |
|---|
| 293 | | helma.skin.parseSkin(options.inline, function(part) { |
|---|
| 294 | | skin.push(part); |
|---|
| 295 | | }); |
|---|
| 296 | | FIXME: can't create inline skin on the fly |
|---|
| 297 | | */ |
|---|
| 298 | | var skin = new helma.skin.Skin(options.inline); |
|---|
| 299 | | skin.render(options.locals); |
|---|
| 300 | | } |
|---|
| 301 | | } else { |
|---|
| 302 | | var type = this.determineTemplateType({action:action, format:req.route.format}); |
|---|
| 303 | | if (type === "est") { |
|---|
| 304 | | var resource = this.getTemplateSource({action:action, type:type}); |
|---|
| 305 | | new este.TemplateEngine(resource.getContent()).evaluate(res, context); |
|---|
| 306 | | } else if (type === "ejs") { |
|---|
| 307 | | var resource = this.getTemplateSource({action:action, type:type}); |
|---|
| 308 | | var result = new ejs.EJS(resource.getContent()).render(context); |
|---|
| 309 | | res.write(result); |
|---|
| 310 | | } else if (type === "skin") { |
|---|
| 311 | | helma.skin.render(this.getTemplatePath({action:action, type:type}), context); |
|---|
| 312 | | } else if (type) { |
|---|
| 313 | | helma.skin.render(this.getTemplatePath({action:action, type:type}), context); |
|---|
| 314 | | } else { |
|---|
| 315 | | res.writeln("Couldn't find " + this.getTemplateSource({action:action, type:type || "skin"})); |
|---|
| 316 | | } |
|---|
| 317 | | } |
|---|
| 318 | | } |
|---|