Grails Custom Scaffolding get access to controller name - grails

I am trying to write a custom src/templates/scaffolding/Controller.groovy and was wondering if there was any way to get access to the controller name? Right now it seems like you can only get the "model" class. The reason I need it is I am customizing the render to prefix the templates directory based on the controller.
For instance I have a controller named AuthorAdminController and I need to customize the list to use the /admin/user/** directory.
Let me know if you have any questions. I am getting ready to look into how to customize DefaultGrailsTemplateGenerator but I am not sure if that is the correct route to go.
Example:
class UserAdminController {
static scaffold = User
}
Currently in my Controller.groovy I get className='user' so I have no access to the controller.

I don't think you can, as the way scaffolding works your template will always be generating a class named DomainClassNameController (i.e. UserController in your example), which gets loaded into a new classloader and then the metaclass of the real controller (UserAdminController) gets new actions added to it which delegate to an instance of the generated UserController.
Now every controller has access to the controllerName property during execution of actions, so this may provide you with a workaround. I haven't tried it, but you could try putting a log.info("controller: \${controllerName}") into the template and see which name it gives you (the backslash to make it resolve at runtime rather than generation time).

Related

Grails controller scaffold default view not displaying the data in the controller

I have created several controllers in my Grails project and put data for each controller in the bootstrap, but the data does not appear in the table for each controller that scaffold provides as the default view. I have checked inside dbconsole to be certain that the data is there, which it is. I have also refreshed the dependencies to make certain that the version of the scaffolding plugin is not corrupted. I am using Grails 2.3.5 and Scaffolding 2.0.1.Are there any suggestions of what could be wrong?
class DepartmentController {
static scaffold=Department
def index() { }
}
Looking back at other examples of using scaffolding I realize now that I should have taken out the index portion of the code, even though it is empty.
Remove your the index method and make it like this:
class DepartmentController {
static scaffold=Department
}
Put some log messages into the controllers (or debug the app) right after the controller loads the data using the domain class in order to see if the query has resulted in any domain instances.
If not, activate sql logging and check the exact select statement executed. Possibly you have something wrong in your domain mapping so a wrong select stmt is sent to the db

Can't understand what this text is saying

I am following a tutorial and got to this point: http://rubysource.com/building-your-first-rails-application-views-and-controllers/
rails generate controller urls new
The reason we only passed in the new action (instead of new, create,
and show) is because Rails automatically generates a dummy view for
each action included in the generator call. In this case, we only want
a dummy view for the new action, so we exclude the others.
So why we only need to create the controller for new? Can someone plase explain it in a little more details?
The command is used to create the UrlsController with only one method: new.
This command will also automatically create a view file for you in:
app/views/urls/new.html.erb
Had you supplied more arguments like:
rails generate controller urls new create show
You would have gotten:
app/views/urls/new.html.erb
app/views/urls/create.html.erb
app/views/urls/show.html.erb
Since the tutorial only needs the new view it was unnecessary to create the additional views, hence those additional arguments were not added to the generate command.
Later in the tutorial you manually add the create and show methods, but you never add views for those methods (since those methods will not be needing specific views files in this application).
So: what you did was create the controller UrlsController with one method new, and the corresponding view for that method. The rest of the methods you will code in manually later in the tutorial so there was no need to auto-generate anything else (create or show).
This will only create the new action in the controller and should skip the other ones.
EDIT:
It will generate a controller called UrlsController in app/controllers and in that controller there will only be one method called action which corresponds to a route or url called urls/new

Render views without any controller in Play

I am building an application using Play for Model and Controller, but using backbone.js, and client side templating. Now, I want the html templates to be served by Play without any backing controller. I know I could put my templates in the public directory, but I would like to use Play's templating engine for putting in the strings in my template from the message file. I do not need any other data, and hence dont want the pain of creating a dummy controller for each template. Can I do this with Play?
You could create a single controller and pass in the template name as a parameter, but I am not sure if it is a good idea.
public static void controller(String templateName) {
// add whatever logic is needed here
renderTemplate("Controller/"+templateName+".html");
}
Then point all your routes to that controller method. Forget about reverse routing, though.
I think I would still rather have a separate controller method for each template. Remember that you can use the #Before annotation (see Play Framework documentation) to have the message string handling in exactly one place, that is executed before each controller method. By using the #With annotation you can even have this logic in a separate class.
You can use template engine from any place in your code:
String result = TemplateLoader.load("Folder/template.html").render(data);

Is it possible to get the controller and the action (NOT THEIR NAME!!) based on the url?

I have found a dozens of threads about getting the name of the controller and method based on the url, I managed that just as well. Can I get the MethodInfo of the method based on their name automatically from the MVC engine, or do I have to do Type.GetType("Namespace.Controllers."+cname+"Controller").GetMethod(mname)? Which is not so nice, since how do I know the namespace in a framework class? How do I know if the default naming patterns are being observed, or is there a different config in use?
I want to get a "What Would MVC execute?" kind of result....
Is it possible?
EDIT: further info:
I have a framework which uses translatable, data-driven urls, and has a custom url rewriting in place. Now it works perfectly when I want to show the url of a news object, I just write #Url.Content("~/"+#Model.Link), and it displays "SomeNewsCategory/SomeNews" instead of "News/29" in the url, without the need to change the RouteTable.Routes dynamically. However in turn there is a problem when I try to write RedirectToAction("SomeStaticPage","Contact"); into a controller. For that, I need to register a static link in db, have it target "/SomeStaticPage/Contact", and then write
Redirect("~/"+DB.Load(linkid).Link); and that's just not nice, when I have 30 of these IDs. The web programmer guy in the team started registering "fake urls", which looked like this:
public class FakeURL
{
public string Controller;
public string Action;
public int LinkID;
}
and then he used it like Redirect(GetFakeUrl("controller","action")); which did the trick, but still was not nice. Now it got me thinking, if I apply a [Link(linkid)] attribute to each statically linked method, then override the RedirectToAction method in the base controller, and when he writes ReturnToAction("action","controller"), I'll actually look up the attribute, load the url, etc. But I'm yet to find a way to get the methodInfo based on the names of the controller and the action, or their url.
EDIT: I've written the reflection by myself, the only thing missing is getting my application's assembly from inside a razor helper, because the CallingAssembly is the dinamically compiled assembly of the .cshtml, not my WebApplication. Can I somehow get that?
To answer your edit, you can write typeof(SomeType).Assembly, where SomeType is any type defined in code in the project (eg, MvcApplication, or any model or controller)
Also, you can write ControllerContext.Controller.GetType() (or ViewContext) to get the controller type of the current request EDIT That's not what you're trying to do.
I found out that it was totally wrong approach. I tried to find the type of the controller based on the name, when instead I had the type all along.
So instead of #Url.Action("SomeAction","SomeController") I'll use #Url.MyAction((SomeController c)=>c.SomeAction()), so I won't even have to find the controller.

Themeing and Master Pages

I have the requirement to support themeing of my site's pages. The way I am doing this is by dynamically choosing a master page based on the current theme.
I have setup a directory structure like so
/shared/masterpages/theme1/Master1.master
/shared/masterpages/theme1/Master2.master
/shared/masterpages/theme1/Master3.master
/shared/masterpages/theme2/Master1.master
/shared/masterpages/theme2/Master2.master
/shared/masterpages/theme2/Master3.master
And I am still using the page directive in the view
<%# Page Title="" Language="C#" MasterPageFile="~/Views/shared/masterpages/theme1/Master1.Master"%>
I would still like to leverage the view's MasterPageFile property and just change the theme directory.
I can only think of three ways to do this none of them which sound great.
Create a custom BaseView class that uses OnPreInit to change the theme like this
Create some xml file or database table that links each view to a master page file and then set this in the controller.
Build some tool that reads all the views and parses them for their masterpagefile, (similar to 2 but could be done at run time potentially.)
Option 1 seems the best option to me so far. Does anyone else have any thoughts on how to do this?
Updated suggestion:
Since my original suggestion didn't work out as I had expected, here's a possible way to work around it, while still keeping your action methods clean, and minimizing repetition of code:
Create an ActionResult that adds the master name/theme name/whatever info you need to pick the correct master page into ViewData["masterInfo"] (or something similar).
Create a base class which themeable views inherit. Your base class should, of course, inherit from System.Web.Mvc.ViewPage. If you need, also create a generic version that inherits from .ViewPage<T>.
In your base class, create a construction method that selects the correct master page based on ViewData["masterInfo"]. I'm not sure if there's a need or not, but don't forget to run the base constructor, either before or after your code, if there is one that needs to run.
Decorate all relevant actions with the attribute, and set their views to inherit your base class instead of System.Web.Mvc.ViewPage.
Original post:
Why not have an ActionFilter, that can be applied on controller level, that sets the MasterPageFile property of the view? If you override OnActionExecuted, it shouldn't be too tricky to test if the result was a ViewResult and in that case change the property to the correct value.

Resources