Grails

Improve support for Controllers to be placed in sub-folders/packages.

Details

  • Type: Sub-task Sub-task
  • Status: Open Open
  • Priority: Minor Minor
  • Resolution: Unresolved
  • Affects Version/s: 0.3, 0.4
  • Fix Version/s: 3.0
  • Component/s: Controllers
  • Labels:
    None

Description

Add support for placing controllers in sub-folders/packages within the controllers folder so you can keep related controllers together, create more structured urls and make path based url security easier?

For example:

\grails-app\controllers\SiteController.groovy -> http://localhost:8080/sample/site
\grails-app\controllers\admin\UserController.groovy -> http://localhost:8080/sample/admin/user
\grails-app\controllers\admin\RoleController.groovy -> http://localhost:8080/sample/admin/role

And views would obviously have to follow the package structure of their controller:

\grails-app\views\admin\user\show.gsp

Issue Links

Activity

Hide
Victor Volle added a comment -

I have created this clone of GRAILS-221, because:

This improvement as described in GRAILS-221 is only half of the enhancement, and its not leading anywhere.
A package is a namespace and not only a name.
The reason that controllers, domain classes should be in packages is exactly that multiple teams would be able to work in parallel if each team has its own package, and must not consider name clashes.

Show
Victor Volle added a comment - I have created this clone of GRAILS-221, because: This improvement as described in GRAILS-221 is only half of the enhancement, and its not leading anywhere. A package is a namespace and not only a name. The reason that controllers, domain classes should be in packages is exactly that multiple teams would be able to work in parallel if each team has its own package, and must not consider name clashes.
Hide
Graeme Rocher added a comment -

We're unlikely to support this for multiple reasons. First of all if you want those kinds of url patterns then you use the URL mappings feature http://grails.org/URL+mapping

Second having name spaces at the URL level has wider implications for other parts of the system. Such as scaffolding. For example if you have a domain class foo.bar.Hello what table does it now map to? Is it foo_bar_hello by default?

Back to URL mapping now. How does one reference a controller in a package when doing url mapping? It starts to get messy and ugly. I'm strongly of the belief that the current solution is as far as we should take it. As for multiple teams working, i've participated in many multi-team projects and never heard of one that involves no communication between teams

Show
Graeme Rocher added a comment - We're unlikely to support this for multiple reasons. First of all if you want those kinds of url patterns then you use the URL mappings feature http://grails.org/URL+mapping Second having name spaces at the URL level has wider implications for other parts of the system. Such as scaffolding. For example if you have a domain class foo.bar.Hello what table does it now map to? Is it foo_bar_hello by default? Back to URL mapping now. How does one reference a controller in a package when doing url mapping? It starts to get messy and ugly. I'm strongly of the belief that the current solution is as far as we should take it. As for multiple teams working, i've participated in many multi-team projects and never heard of one that involves no communication between teams
Hide
Victor Volle added a comment -

Hi

I understand that you want to keep Grails as simple as possible. So if you do not support this, it will just narrow down the number of projects where I would consider using Grails, but perhaps this is even neccesary (to keep Grails simple). But ...

1) URL: I do not really understand the issue here. Yes it has implications, but what are the problems.

2) Domain class => table name: Why not map foo.bar.Hello to foo_bar_Hello as the default case (as long as I can override), you do not have to use mutliple packages, Maybe all domain classes in a package share some common configurable prefix? Maybe only the last package name shall be used as part of the table name – whatever.

3) URL (again): I have not looked too thoroughly into the URL mapping stuff, so I refrain here

4) Multiple teams:
no communication between teams: what a horror.
communication that ain't necessary: what a waste of time ... that's why I prefer to have name spaces that give teams their own "fiefdom"

Victor

Show
Victor Volle added a comment - Hi I understand that you want to keep Grails as simple as possible. So if you do not support this, it will just narrow down the number of projects where I would consider using Grails, but perhaps this is even neccesary (to keep Grails simple). But ... 1) URL: I do not really understand the issue here. Yes it has implications, but what are the problems. 2) Domain class => table name: Why not map foo.bar.Hello to foo_bar_Hello as the default case (as long as I can override), you do not have to use mutliple packages, Maybe all domain classes in a package share some common configurable prefix? Maybe only the last package name shall be used as part of the table name – whatever. 3) URL (again): I have not looked too thoroughly into the URL mapping stuff, so I refrain here 4) Multiple teams: no communication between teams: what a horror. communication that ain't necessary: what a waste of time ... that's why I prefer to have name spaces that give teams their own "fiefdom" Victor
Hide
Marc Palmer added a comment -

I see exactly why this is a problem for the implementation. There are numerous reasons why the "name" string used internally for a class does not allow dots or similar, and that these are used in many ways to implement conventions.

I do not think it is a big problem for grails to insist on unique class names in you grails class graph.

Show
Marc Palmer added a comment - I see exactly why this is a problem for the implementation. There are numerous reasons why the "name" string used internally for a class does not allow dots or similar, and that these are used in many ways to implement conventions. I do not think it is a big problem for grails to insist on unique class names in you grails class graph.
Hide
Simon Briman added a comment -

1. URL: Imagine that you have an app where user's front-end and admin's back-end differ so that they're better server by different set of controllers and views. Currently what we do? We create FooController and AdminFooController, create views under adminFoo dir and add a URL mapping "/admin/foo/$action?/$id?" { controller = "adminFoo" }. Now "rinse and repeat" for bar, baz, etc... Ugly as my life.
If we had packages we could create FooController and admin.FooController, create views under admin/foo and no URL mappings at all. Just cleaner.

2. Domain class => table name: It feels quite organic to me to map foo.bar.Hello to foo_bar.hello by default where foo_bar is a DB schema name.

3. Rails has it. )))

Show
Simon Briman added a comment - 1. URL: Imagine that you have an app where user's front-end and admin's back-end differ so that they're better server by different set of controllers and views. Currently what we do? We create FooController and AdminFooController, create views under adminFoo dir and add a URL mapping "/admin/foo/$action?/$id?" { controller = "adminFoo" }. Now "rinse and repeat" for bar, baz, etc... Ugly as my life. If we had packages we could create FooController and admin.FooController, create views under admin/foo and no URL mappings at all. Just cleaner. 2. Domain class => table name: It feels quite organic to me to map foo.bar.Hello to foo_bar.hello by default where foo_bar is a DB schema name. 3. Rails has it. )))
Hide
Graeme Rocher added a comment -

Moving non-critical issues that aren't going to make it into 1.1 to 1.2

Show
Graeme Rocher added a comment - Moving non-critical issues that aren't going to make it into 1.1 to 1.2
Hide
James Frost added a comment -

In a medium size project (for us) we have 50 odd controllers defined. Like Simon, we have to start using a naming convention for our controllers to avoid duplication (the Admin prefix is a perfect example for us too) and to help organise the source tree (which is a mess with 50 groovy files in the same folder)

As it happens, I'm not bothered much with the domain class/GORM side of things because we don't use GORM for much - most of our Grails apps are backed with Java REST services - but I understand the issues with the name -> table name mappings. Personally I'd stick with the same pattern as currently - I'd be quite happy for admin.CustomerController to still map to the customer table by default, as long as we could override it with a static mapping or otherwise.

Another big issue is plugins. We have a dozen or so plugins we use in the project (some ours, some third party). Since all the controllers and services etc are in the default namespace, clashes in naming is even more of an issue here and takes very careful management. If you could solve this issue for plugins (perhaps some kind of default namespace based on the plugin name?) I'd be happy to keep to the single package for the application and break lots more functionality out into individual plugins.

James

Show
James Frost added a comment - In a medium size project (for us) we have 50 odd controllers defined. Like Simon, we have to start using a naming convention for our controllers to avoid duplication (the Admin prefix is a perfect example for us too) and to help organise the source tree (which is a mess with 50 groovy files in the same folder) As it happens, I'm not bothered much with the domain class/GORM side of things because we don't use GORM for much - most of our Grails apps are backed with Java REST services - but I understand the issues with the name -> table name mappings. Personally I'd stick with the same pattern as currently - I'd be quite happy for admin.CustomerController to still map to the customer table by default, as long as we could override it with a static mapping or otherwise. Another big issue is plugins. We have a dozen or so plugins we use in the project (some ours, some third party). Since all the controllers and services etc are in the default namespace, clashes in naming is even more of an issue here and takes very careful management. If you could solve this issue for plugins (perhaps some kind of default namespace based on the plugin name?) I'd be happy to keep to the single package for the application and break lots more functionality out into individual plugins. James
Hide
Victor Volle added a comment -

I would really propose to either close this as "won't fix" or finally getting to implement it.
Postponing it for more than two years (and this is a clone of a previous issue) is not credible.

Show
Victor Volle added a comment - I would really propose to either close this as "won't fix" or finally getting to implement it. Postponing it for more than two years (and this is a clone of a previous issue) is not credible.
Hide
Graeme Rocher added a comment -

Its still open because it has merit, and we will get to it eventually. But it is a big change and the focus for Grails 1.2 was incremental improvements and stability. I'm there fore assigning it to Grails 2.0 since there may be breakages associated with changes this.

Show
Graeme Rocher added a comment - Its still open because it has merit, and we will get to it eventually. But it is a big change and the focus for Grails 1.2 was incremental improvements and stability. I'm there fore assigning it to Grails 2.0 since there may be breakages associated with changes this.
Hide
Felipe Rodrigues added a comment -

Why not a simple namespace attribute to be configured on the controller telling it should belong to a namespace? Them the packages wouldn't have to be used for that purpose. I was thinking in something like:

class FooController { ... }
class AdminFooController {
def namespace = "admin"
}

Then the url mappings could check that attribute and if it is there, use the indicated namespace. This is not a big change I guess.

Show
Felipe Rodrigues added a comment - Why not a simple namespace attribute to be configured on the controller telling it should belong to a namespace? Them the packages wouldn't have to be used for that purpose. I was thinking in something like: class FooController { ... } class AdminFooController { def namespace = "admin" } Then the url mappings could check that attribute and if it is there, use the indicated namespace. This is not a big change I guess.
Hide
Maxim Borkunov added a comment -

Sometime, I believe, they will fix it.

Show
Maxim Borkunov added a comment - Sometime, I believe, they will fix it.
Hide
Gerald Boersma added a comment -

I hope not, because I believe Graeme is correct.

I am familiar with Ruby, and IMO the Ruby way is broken. Automatically generating URLs based on controller packaging ties the external view of the application to the internal structure, and these should be independent.

In Groovy, you can organize controllers / domain classes in packages and map URLs accordingly. The mapping between the external view and the internal view is under your control.

That being said, it would be nice to extend the packaging concept to views to be able to organize folders under views in the same structure as controller packaging.

Show
Gerald Boersma added a comment - I hope not, because I believe Graeme is correct. I am familiar with Ruby, and IMO the Ruby way is broken. Automatically generating URLs based on controller packaging ties the external view of the application to the internal structure, and these should be independent. In Groovy, you can organize controllers / domain classes in packages and map URLs accordingly. The mapping between the external view and the internal view is under your control. That being said, it would be nice to extend the packaging concept to views to be able to organize folders under views in the same structure as controller packaging.
Hide
Kim A. Betti added a comment -

"That being said, it would be nice to extend the packaging concept to views to be able to organize folders under views in the same structure as controller packaging."

This's exactly what I'm missing.

Show
Kim A. Betti added a comment - "That being said, it would be nice to extend the packaging concept to views to be able to organize folders under views in the same structure as controller packaging." This's exactly what I'm missing.
Hide
Lauri Lüüs added a comment -

In asp.net MVC there is this feature called Areas. It works beatufully. Developer can "package" similar features into specific area eg.

So to separate controllers into specific area can be done with custom url mappings. But the problem is with the views. I would like to see that my project view folder would be also organized into logical structure not just huge list of folders.

I even accept that in controller I would have some static variable to configure default view path for actual controller, that would look something like

package admin
class AdminSettingsController {

def static default_view_folder = "/admin/settings"
def index={}
}

and my admin index gsp location would be

grails-app/views/admin/settings/index.gsp

Show
Lauri Lüüs added a comment - In asp.net MVC there is this feature called Areas. It works beatufully. Developer can "package" similar features into specific area eg. So to separate controllers into specific area can be done with custom url mappings. But the problem is with the views. I would like to see that my project view folder would be also organized into logical structure not just huge list of folders. I even accept that in controller I would have some static variable to configure default view path for actual controller, that would look something like package admin class AdminSettingsController { def static default_view_folder = "/admin/settings" def index={} } and my admin index gsp location would be grails-app/views/admin/settings/index.gsp
Hide
Kim A. Betti added a comment -

I've started playing with a proof of concept plugin that implements the kind of feature Lauri is talking about.
https://github.com/kimble/grails-view-compartments

Show
Kim A. Betti added a comment - I've started playing with a proof of concept plugin that implements the kind of feature Lauri is talking about. https://github.com/kimble/grails-view-compartments
Hide
Christopher Rudolf added a comment - - edited

Lets declare what controllers are allowed to be resolved for a given url pattern: If we restrict the controller's package for a URL mapping, then it would become very handy.

The following solution is a bit dirty - however it works and forbids admin-controllers to appear in the root folder of the application.

class UrlMappings {
	
	static mappings = {
		// admin/-folder is mapped to all controllers within the com.test.admin-package
		"/admin/$controller/$action?/$id?" {
			controller(validator: {
				try {
					Class.forName('com.test.admin.'+controllerName+'Controller', true,
						Thread.currentThread().contextClassLoader);
				} catch(Exception e) {
					return false;
				}
				return true;
			})
                // /-folder is mapped to all controllers within the com.test.public-package
		"/$controller/$action?/$id?" {
			controller(validator: {
				try {
					Class.forName('com.test.public.'+controllerName+'Controller', true,
						Thread.currentThread().contextClassLoader);
				} catch(Exception e) {
					return false;
				}
				return true;
			})
	}
[..]
}

If you are fine with this approach, lets make the code more clean (is there a possibility to access the controller's class, which is resolved from grails instead of verifying whether the class exists?) and add a standard validator which is like:

"/admin/$controller/$action?/$id?" {
		constraints {
			controller(package: "/com.test.admin.*/")
		}
	}
	"/$controller/$action?/$id?" {
		constraints {
			controller(package: "/com.test.public.*/")
		}
	}
Show
Christopher Rudolf added a comment - - edited Lets declare what controllers are allowed to be resolved for a given url pattern: If we restrict the controller's package for a URL mapping, then it would become very handy. The following solution is a bit dirty - however it works and forbids admin-controllers to appear in the root folder of the application.
class UrlMappings {
	
	static mappings = {
		// admin/-folder is mapped to all controllers within the com.test.admin-package
		"/admin/$controller/$action?/$id?" {
			controller(validator: {
				try {
					Class.forName('com.test.admin.'+controllerName+'Controller', true,
						Thread.currentThread().contextClassLoader);
				} catch(Exception e) {
					return false;
				}
				return true;
			})
                // /-folder is mapped to all controllers within the com.test.public-package
		"/$controller/$action?/$id?" {
			controller(validator: {
				try {
					Class.forName('com.test.public.'+controllerName+'Controller', true,
						Thread.currentThread().contextClassLoader);
				} catch(Exception e) {
					return false;
				}
				return true;
			})
	}
[..]
}
If you are fine with this approach, lets make the code more clean (is there a possibility to access the controller's class, which is resolved from grails instead of verifying whether the class exists?) and add a standard validator which is like:
"/admin/$controller/$action?/$id?" {
		constraints {
			controller(package: "/com.test.admin.*/")
		}
	}
	"/$controller/$action?/$id?" {
		constraints {
			controller(package: "/com.test.public.*/")
		}
	}

People

Vote (55)
Watch (51)

Dates

  • Created:
    Updated:
    Last Reviewed: