Grails 2.4.4 here. All of my controllers were generated using the standard grails create-controller ... command:
class FizzController {
def index() {
// ...
}
}
class BuzzController {
def index() {
// ...
}
}
...etc.
At the top of each action method, in each controller, I need to call a method doSomething(String). This method needs to have access to redirect(...) so that it can redirect the user if need be. Ideally, I could create an abstract base controller class and have all my controllers extend it:
abstract class BaseController {
void doSomething(String cmd) {
if(cmd == 'Yee haw!') {
redirect(controller: 'foo', action: 'baz')
return false
}
}
}
class FizzController extends BaseController {
def index() {
String cmd = getCmdSomehow()
doSomething(cmd)
// ...etc.
}
}
class BuzzController extends BaseController {
def index() {
String cmd = getCmdSomehow()
doSomething(cmd)
// ...etc.
}
}
However I'm not sure Grails will allow this, since it's doing "Groovy magic" under the hood to make sure there is access to things like redirect(...) and render(...), etc. It would also be wonderful if I could put all this in a closure and just have it executed implicitly (without having to call doSomething() at the beginning of each and every controller action.
What is my best option/solution here? Please use a concrete code example!
Use Filter instead:
class ExampleFilters {
def filters = {
logFilter(controller: '*', action: '*') {
before = {
}
after = { Map model ->
}
afterView = {
}
}
}
}
Related
Basically I want to make code generic and use different Services upon given Route parameters.
What is the proper and working way to achieve this?
The following works:
Routes:
Route::get('socialmediaAccount/authorize/twitter', function(TwitterApi $client){ return ['Works'];});
That works as well:
Routes
Route::get('socialmediaAccount/authorize/twitter', ['uses' => 'SocialmediaController#authorizeAccount']);
Controller
class SocialmediaController extends Controller
{
public function authorizeAccount(TwitterApi $client)
{
return ['Works as well'];
}
}
Now I want to add facebook, the idea is this:
Routes
Route::get('socialmediaAccount/authorize/{type}', ['uses' => 'SocialmediaController#authorizeAccount']);
Controller
class SocialmediaController extends Controller
{
public function authorizeAccount($type)
{
if($type == 'twitter') {
$client->call TwitterApi-method(); //????????????
return ['???'];
}
if($type == 'facebook') {
$client->call FacebookApi-method(); //????????????
return ['???'];
}
}
}
Since this didn't work I tried and failed with following:
Now in the definition of my Controller method I can't use the Type Hint anymore, and if I try to create separate methods ala authorizeTwitter I can't call it with Type Hint. I tried following:
Routes - is the same
Controller
class SocialmediaController extends Controller
{
public function authorizeAccount($type)
{
if($type == 'twitter') {
$this->authorizeTwitter();
return ['???'];
}
if($type == 'facebook') {
$this->authorizeFacebook();
return ['???'];
}
}
private function authorizeTwitter(TwitterApi $client) //????????????
{
call TwitterApi-method();
}
private function authorizeFacebook(FacebookApi $client) //????????????
{
call TwitterApi-method();
}
}
The error here is 'Argument 1 passed to ... must be an instance of ... TwitterApi, none given.
You can't use dependency injection on methods that you call manually. Only on route methods, because route methods will be resolved automatically.
This said the following should work:
class SocialmediaController extends Controller
{
public function authorizeAccount($type, TwitterApi $twitterClient, FacebookApi $facebookClient)
{
if($type == 'twitter') {
// do Twitter authorization using $twitterClient here
}
if($type == 'facebook') {
// do Facebook authorization using $facebookClient here
}
}
}
I have the two domain clases:
class Persona {
String nombre
String apellidos
static hasMany = [pertenencias: Pertenencia]
static constraints = {
}
static mapping = {
pertenencias cascade: "all-delete-orphan"
}
}
class Pertenencia {
String nombre
static belongsTo = [persona:Persona]
static constraints = {
}
}
The service:
class MembresiaService {
#Transactional
def saveAll() {
def p = new Persona(nombre: 'carlos', apellidos: 'gm')
p.addToPertenencias(nombre: 'auto')
p.addToPertenencias(nombre: 'computadora')
p.addToPertenencias(nombre: 'casa')
p.save()
}
#Transactional
def deletePertenencias() {
def p = Persona.get(1)
p.pertenencias?.clear()
}
}
And the controller:
class TestController {
def membresiaService
def index() {}
def saveAll() {
membresiaService.saveAll()
redirect(action: "index")
}
def deletePertenencias() {
membresiaService.deletePertenencias()
redirect(action: "index")
}
}
When I execute saveAll() method from controller it saves the data in the database, when I execute deletePertenencias() from controller it deletes the "pertenecias" collection of Persona from the database (as expected).
I have installed the Grails console plugin , first time I execute the lines of saveAll() service method in the console, the result is the "persona" and its "pertenencias" in database. Then I execute the lines of deletePertenencias() service method in console but it doesn't delete the data of database and the "persona" object mantains the "pertenencias" (as if I had not run deletePertenencias() code).
Anyone kwnow why the code executed from console gives unexpected results?
I expect the result was the same from controller and console but the behaviour is different.
I have a Grails service,
Parent Class:
class BarService{
def fetchData(params) {
return fooData.toString()
}
}
Child Class:
class FooService extends BarService{
def fetchData(params) {
def fooData = super.fetchData(params) //this will call the BarService method
return fooData
}
}
Is this the right groovy way of doing it? Because to me this looks like Java
Thanks
As per your example, there is not much that can be done, except maybe removing the optional return keyword:
// Parent Class:
class BarService{
def fetchData(params) {
params.fooData.toString()
}
}
// Child Class:
class FooService extends BarService{
def fetchData(params) {
super.fetchData params
}
}
assert new FooService().fetchData([fooData: 900]) == "900"
The "return" keyword isn't the problem (as U can see here - https://groovyconsole.appspot.com/edit/5158539458772992). If You're getting a "null", the problem is the code at:
return fooData.toString()
Should be like the #WillP said (with "return" keywork optionally):
return param.fooData.toString()
Given this mapping:
"/student/degree/$studentid/$degreecode"(controller:'student', action:'degree')
I am trying the tests below and both fail with AssertionFailedError: '...did not match any mapping' and both are valid URLs that do function. I can succeed with a test URL of just '/student/degree' which I think should fail as the parameters are required.
It seems the assertURL methods are not handling multiple parameters. Does this only work for 'id' and is it not possible to create tests for these URLs?
#TestFor(UrlMappings)
#Mock([StudentController])
class UrlMappingsTests {
void testUrlMappings() {
assertForwardUrlMapping("/student/degree/102345678/N", controller: "student", action: "degree") {
assertForwardUrlMapping("/student/degree/102345678/N", controller: "student", action: "degree") {
studentid = 102345678
degreecode = "N"
}
}
}
}
Tested with Grails 2.2.4, and it works for me, I just add a custom mappings class:
package test
class MyUrlMappings {
static mappings = {
"/test/show/$myId/$myVal"(controller: "test", action: "show")
}
}
import spock.lang.*
import test.MyUrlMappings
#TestFor(MyUrlMappings)
#Mock([TestController])
//I'm using Spock
class MyUrlMappingsSpec extends Specification {
void "mapping should consider myId and myVal"() {
expect:
assertForwardUrlMapping("/test/show/1/tst", controller: "test", action: "show") {
//all params considered as String
myId = '1'
myVal = 'tst'
}
}
}
earlier I use Spring MVC and annotation #ModelAttribute.
#Controller
public class ArticleController {
#ModelAttribute("articles")
public List<Testset> getArticles(){
return articleDao.all();
}
#RequestMapping("/first.htm")
public void first(){
}
}
How can made this behaviour in Grails controller?
class ArticleController{
//this variable I want to in every action
List articles
def first = { }
def second = { }
def third = { }
}
Of course I can use this code for every action
def first = {
this.articles = Article.all()
}
but I want not this.
Thank very much for your help.
Tomáš
You can define an after interceptor and add data to the model there:
class ArticleController {
def afterInterceptor = { model ->
model.articles = Article.list()
}
def first = { }
def second = { }
def third = { }
}
The docs for this are here: http://grails.org/doc/latest/ref/Controllers/afterInterceptor.html