404
Error: server received POST request but was unable to a proper action

vs: http://knowyourmeme.com/memes/i-accidentally

Submitted by Sverre Rabbelier on 28 November 2010 at 18:46

On 28 November 2010 at 22:03 Sverre Rabbelier commented:

See also 6d9ae73f451

Submitting the form on that page demonstrates the breakage. Moving the var declaration inside the action statement fixes it, as well as removing the templateElsewhere call.

The file where the typo is defined is org/webdsl/dsl/to-java-servlet/java-page.str.


On 29 November 2010 at 10:27 Danny Groenewegen commented:

I am able to reproduce it, thanks for the report. Note that you will probably need to move the var declaration inside the action anyway (or at least setting the course), otherwise each view of the broken page saves a new Evaluation due to the inverse association between Course and Evaluation (the persisted Course will have a reference to a transient Evaluation which will then be saved).


On 29 November 2010 at 13:32 Sverre Rabbelier commented:

That doesn’t seem like TRTTD (TM), it should not create a new entity until I call .save(), which is the WebDSL idiom for creating new entities, no? Of course, when I do call save(), then it should add all the inverse associations.


On 29 November 2010 at 14:13 Danny Groenewegen commented:

The idiom for creating new entities is that you either call .save(), or you make an an already persisted entity refer to the new entity.

In an earlier version of WebDSL we tried having explicit saves for each new entity, however, we found that this was verbose (lots of save calls) and error-prone (easy to forget one of them). Minimizing differences between creating and editing entities also makes templates more reusable.

I think that postponing the inverse association would make things more confusing, e.g. what if the inverse property is in the input, then you wouldn’t see the value you just assigned to it.


On 29 November 2010 at 14:52 Sverre Rabbelier commented:


class Education {
courses -> Set(inverse=Course.education)
}

class Course {
education -> Education
foo :: String
}

define page example(edu : Education) {
var course := Course {
education := edu
}

action saveCourse(c : Course) {
    c.save();
}

form {
    label("Foo") { input(course.foo) }
    label("Education") { input(course.education) }
    label("Demo") { output(edu.courses) }
    submit saveCourse(course) { "Save" }
}

}

I agree that making an existing entity refer to an entity should save it. However, I think in the above example the form should show two input fields, an empty one for foo, and a dropdown for education with the ‘edu’ course preselected. In the database, edu.courses should not yet include the new course, and there should not yet be a new course until save is called. The implication is that the output does not show the new course even if validation fails, which only makes sense, since the entity was not yet saved. Only when the course is saved should edu.courses be updated to include the new course.


On 29 November 2010 at 15:38 Danny Groenewegen commented:

You could also say the output should include the new course because it has an inverse. I’m not convinced having multiple semantics for object creation (Course{education := edu}) would improve things. But I agree that inverse can be tricky right now, also e.g. when you try to keep old versions of entities. In those cases it’s better to not use inverse at all, perhaps also for your example.

The 404 was also caused by this problem, the shown form is for the saved course instance, but upon submitting it is trying to write to a different course instance (different identifiers, which are used in the submit name).
For now, I’ve added a warning to the compiler that warns when assigning to inverse properties in template vars.

I’ve also fixed the error message.


On 2 December 2010 at 00:44 Sverre Rabbelier commented:

Everytime I want to create a new entity I have to double check whether any of it’s fields are an inverse property (so that I know whether or not to call save)!

Consider the asymmetry in case:

			var studentAnswer := EvasysStudentAnswer {
				evasys := e
			};
			studentAnswer.save();

			var idx := 0;
			for(idx : Int from 0 to questions.length) {
				var evasysAnswer := EvasysAnswer {
					question := questions.get(idx)
					answer := answerTexts.get(idx).parseInt()
				};				
			}

There is no visual or cognitive distinction between EvasysStudentAnswer and EvasysAnswer, yet they have to be treated differently! This is totally counter intuitive :)

I agree with cascading saves (if EvasysAnswer happened to contain a reference to StudentAnswer, then I wouldn’t have to save StudentAnswer up above, since it needs to be saved for EvasysAnswer to refer to it). Just not the implicit saves if an entity has an inverse property.


On 2 December 2010 at 14:17 Danny Groenewegen commented:

(Where are the entity definitions?)

You can safely call save() on an entity that is already being saved due to cascading, so you can treat them similarly. The main issue is accidentally saving an entity.

Log in to post comments