Using a grails/groovy class by reference - grails

I'm trying to create my own CRUD controller in grails for the stuff that the scaffolding won't do.
Instead of maintaining code for a controller for each domain, I'd like to have one controller that can look after any domain for the generic CRUD calls.. as the only difference is the domain class name.
Using the example of domain class Job & Note
Instead of
Job.get(id)
Job.list()
def instance = new Job(params)
Note.get(id)
Note.list()
def instance = new Job(params)
I was thinking of
def someHandler = Job // configurable
someHandler.get(id)
someHandler.list()
def instance = new someHandler(params)
The first two static methods work fine (get, list) but creating a new instance does not.
Any pointers as to how to best do this.
Cheers

Call the default constructor using
def instance = someHandler.newInstance()
and the constructor for params using
def instance = someHandler.newInstance(params)

If you're not happy with the scaffolded controllers/views that Grails provides by default, and want to change them in a similar fashion for all domain classes, a better approach might be to simply edit the templates that are used to generate these controllers/views.
You can do this by running the script grails install-templates. This will create a number of files in the src/templates/scaffolding directory, each of which defines the template used to generate a scaffolded artifact.
Change these templates to create the controllers/views that you want. If you've already run grails generate-all for any domain classes, you'll need to run it again for those classes to update the existing scaffolding.

Related

How do you call a constructor from using getArtefact in Grails 3

I need a list of my domains, and I am getting them by using:
def domains = grailsApplication.getArtefacts("Domain")*.clazz
In each of my domains is a constructor, I want to do something like this:
def item = new domains[0](object)
item.save()
essentially making a generic save.
I figured out a way to invoke the constructor. I don't know if it is the best way to do it. Example code below:
//provides all Domain classes in your project in an ArrayList
//You can also use getArtefact("Domain", "classname")*.clazz to return a subset
def domains = grailsApplication.getArtefacts("Domain")*.clazz
def domain = domains[0].newInstance(object) //Whatever your constructor wants variable: "item" works as well
def domain.save()
Got this working thanks to http://mrhaki.blogspot.ca/2010/06/groovy-goodness-create-class-instance.html

grails view parameters bind with non domain class

I want to bind view page params to non domain class(say *.java class).
Project projectInstance = new Project(params)
where Project is not Domain class, it is simply java class.
Does grails provide any plugin or any way to bind it to non domain class.
Thanks.
You cand try with bindData.
In controler:
Project project = new Project()
bindData(project, params)

Grails - #Transactional annotation on generating simple Controller

Grails newb here. I was using GGTS to generate a controller for a simple User model (string user, string passed)
On looking at the GGTS generated code, I noticed that there is #Transactional annotation on certain actions like delete(), save() and update().
My question is, why is #Transactional used in the controller and why is #Transactional not used in Services?
Newb in grails
In grails the correct way to do all DB related work and DB transactions is to do in service classes and not controllers. So by default controllers don't have privileges to do transaction and hence we use #Transactional annotation. While in services by default transaction is allowed.
If you use a service method which does some DB transaction then you need to make your controller action transactional by #Transactional annotation. While no need to mention it in service method.
Other way round if you donot make the controller transactional then you will need to mention it in service class.
Best practice will be not to use auto generated code as it will be less maintainable for a beginner. Official documentation will be your best help and while doing it when you will create controller you will not find any transactional annotation while when you will create service class you will see transactional annotation at the class level.
P.S. - 'Create' class is not equal to 'generate'.

Where is the "magic" happening when a Controller instantiates the correct implementation of the Interface in Web API DI?

It seems to me that somewhere a rabbit is being pulled out of a hat when it comes to DI in Web API Controllers.
I grok that:
0) The Controller in a Web API project can be called with various classes to be instantiated, all of which implement the interface on which the Controller depends. e.g., with this Controller code:
private readonly IDepartmentRepository _deptsRepository;
public DepartmentsController(IDepartmentRepository deptsRepository)
{
if (deptsRepository == null)
{
throw new ArgumentNullException("deptsRepository is null");
}
_deptsRepository = deptsRepository;
}
..."deptsRepository" can be a class that implements IDepartmentRepository and retrieves test data, OR it can be a class that implements IDepartmentRepository and retrieves production data, OR (etc.)
1) Web API decides which Controller is called based on the URI the client calls, and that Web API decides which method in that Controller is called based on what type (GET, POST) etc. it is and what args, if any, are passed with it.
2) Castle Windsor intercepts this automatic control of Controllers with its own replacement routing engine.
What I don't grok is just where the developer injects the concrete class that implements the interface expected by the Controller. IOW, if I want to run the class that pulls from test data, where do I add code to specify that? I would think it would be somewhere in Global.asax.cs, something like (pseudocode):
// Use test data for now
DeptsControllerClass = TestDataClass;
//DeptsControllerClass = ProductionDataClass;
IOW, where does one specify, "This time, I want you to inject THIS concrete class which implements the required interface"?
As you wrote at point 1, Routing and IoC are two different things.
Once the routing engine figures out which controller has to be invoked, a "controller factory" will be invoked.
WebApi framework allows to plug your own factory as following:
GlobalConfiguration.Configuration.Services.Replace(
typeof(IHttpControllerActivator),
new WindsorCompositionRoot(this.container));
Read more on Mark Seemann post about webapi&windsor plumbing.
Which concrete will be used to satisfy a given interface dependency, that's up to the ioc you choose to use within your factory as above. Using Windsor you may/should link interfce&concrete in the Installers.
Let me try to recap the whole process
1) Set up the routing in order to link url vs controller
2) create the container and register all components using installers
3) replace default controller factory w/ a factory working w/ your favorite ioc container(Windsor, I presume :-) )
4) Enjoy the magic.
All those steps happend in the Application_start within the Global.asax

Do I have to use webflows?

I want to collect the data for a domain class over a few forms. I'd like to initialise an instance of the domain, carry it through the form pages (assigning the collected data to the properties of the instance) and save the instance after the last form is completed successfully.
Is there a way to do this without webflows?
Is there a way to do this without webflows?
You can use hidden fields to accomplish this. But I may prefer you to use Webflows.
Here are some advantages of using Webflows:
1)You got two new scopes flow and conversation allows you to store variables, which are accessed within your flow
2)You have simple DSL to keep things tidy
3)Since there is a flow scope, you can do something like this:
flow.someThing = new YourClassName(params) //places object in flow scope
Keep in Mind:
1)If you use flow-scoped objects your class need to be implemented Serializable class.
2)And from Grails 1.2, you need to install Webflow plugin explicitly. Document says this:
From Grails 1.2 onwards Webflow is no longer in Grails core, so you
must install the Webflow plugin to use this feature: grails
install-plugin webflow
(see here).
As an alternative to Ant's comment, you could use the session, but storing a non-domain object or a simple Map. This will definitely lead to a lot of extra complexity, and the webflows do provide a lot of protection against accidental back-buttons, etc.
Rough idea:
in grails-app/domain
class Widget {
String name
int id
// constraints, etc
}
in grails-app/controllers
class WidgetCommand {
// setup your command
}
class WidgetController {
def savePage1 = { WidgetCommand cmd ->
// validate, etc
def widget = session.tempWidget ?: [:]
widget.putAll(cmd.properties)
session.tempWidget = widget
[widget: widget]
}
def savePage2 = { WidgetCommand cmd ->
// etc
}
def savePage3 = {
// or something similar here
def finalWidget = new Widget(session.tempWidget)
finalWidget.save()
}
}
You could try storing an actual domain object directly in memory, but I believe that will automatically be saved at session close if you are editing the object (as opposed to new ones), and you'll have to re-link it to the Hibernate session.

Resources