I know I can declare a reusable pure Scala block like this in a template:
#title(text: String) = #{
text.split(' ').map(_.capitalize).mkString(" ")
}
I can now call #title("someString") in the template but this code block is not accessible from outside this template.
How can I declare such a block that is accessible from other templates as well?
I've tried to create a new template title.scala.html like this:
#(text : String)
#{
text.split(' ').map(_.capitalize).mkString(" ")
}
I can now call #title("someString") from any template I want, but this doesn't give me the exact same result as the first block, inside the template (I assume in the first case it returns a String whereas it returns Html in the second case).
I'm using Play framework 2.0.4 and I'm coding in Java (hence my limited Scala knowledge).
Using tags is targeted for building reusable blocks of HTML code, therefore it returns Html
To work easily with common types of data you can easily add a custom Java class (for an example in freshly created utils package (in app directory), and prepare in it all required formatters as a static methods:
utils.MyFormats.java:
package utils;
import org.apache.commons.lang3.text.WordUtils;
public class MyFormats {
public static String capitalize(String str) {
return WordUtils.capitalize(str);
}
public static int sumElements(int a, int b) {
return a + b;
}
}
In template:
<h2>Capitalized each word: #(utils.MyFormats.capitalize("foo bar"))</h2>
<h3>Sum of two integers, 2+3 = #(utils.MyFormats.sumElements(2, 3))</h3>
Related
Go provides great build in HTML templating functionality, however for me it is important that I can ensure certain fields will always be available to templates in my application. A good example of this is a title field, which needs to be shown on every HTML page.
Given the following pages:
Home
Register
Contact
I would likely create the following objects for the templating system:
HomePage struct
RegisterPage struct
ContactPage struct
Is there a recommended way to ensure that the structs for each page have certain fields available to them?
Ideally I would implement this through Polymorphism, but that isn't officially supported in Go. The alternative, embedding doesn't appear to have any enforcement, i.e all of the child structs can embed a parent struct but don't have to.
Let me know if I haven't expressed my question clearly enough.
Executing a template does not enforce anything to the parameters, Template.Execute() accepts a value of type interface{}.
You are the one creating the HomePage, RegisterPage and ContactPage structs. What stops you from embedding a BasePage struct with the required fields? Are you worried you will forget about it? You will notice it at the first testing, I wouldn't worry about that:
type BasePage struct {
Title string
Other string
// other required fields...
}
type HomePage struct {
BasePage
// other home page fields...
}
type RegisterPage struct {
BasePage
// other register page fields...
}
If you want from code to check if page structs embed the BasePage, I recommend another way: interfaces.
type HasBasePage interface {
GetBasePage() BasePage
}
Example HomePage that implements it:
type HomePage struct {
BasePage
// other home page fields...
}
func (h *HomePage) GetBasePage() BasePage {
return h.BasePage
}
Now obviously only pages that have a GetBasePage() method can be passed as a value of HasBasePage:
var page HasBasePage = &HomePage{} // Valid, HomePage implements HasBasePage
If you don't want to use interfaces, you can use the reflect package to check if a value is a struct value and if it embeds another interface. Embedded structs appear and can be accessed like ordinary fields e.g. with Value.FieldByName(), with the type name being the field name.
Example code using reflect to check if a value embeds BasePage:
page := &HomePage{BasePage: BasePage{Title: "Home page"}}
v := reflect.ValueOf(page)
if v.Kind() == reflect.Ptr {
v = v.Elem()
}
if v.Kind() != reflect.Struct {
fmt.Println("Error: not struct!")
return
}
bptype := reflect.TypeOf(BasePage{})
bp := v.FieldByName(bptype.Name()) // "BasePage"
if !bp.IsValid() || bp.Type() != bptype {
fmt.Println("Error: struct does not embed BasePage!")
return
}
fmt.Printf("%+v", bp)
Output (try it on the Go Playground):
{Title:Home page Other:}
So, I'm using the Play Framework in conjunction with MongoDB. I'm attempting to pass a List to my view to loop through and display the rows.
Controller:
public static Result find() {
List<DBObject> results = MongoController.find(MongoController.getLocalConnection("test"), "jobs");
if (results == null) {
redirect("/");
}
return ok(find_job.render(results));
}
The MongoController called above uses the following function (functioning - tried and tested):
public static List<DBObject> find(DB database, String collectionName) {
DBCursor cursor = database.getCollection(collectionName).find();
List<DBObject> items = new ArrayList<DBObject>();
while (cursor.hasNext()) {
items.add(cursor.next());
}
return items;
}
View:
#(results : List[DBObject])
The error I'm receiving is:
Compilation error:
Not found: type DBObject
Does anybody have any pointers please? I apologise - I'm new to this framework.
Twirl templates don't work quite the same way when importing things into the local namespace. By default only the models package, and a few other Play packages are imported into the view namespace. Imports made afterward are not used for the template parameters.
You need to either use the fully-qualified package name:
#(results: List[com.mongodb.DBObject])
Or you can add to the templateImports key in build.sbt. i.e., you'd drop this line into your build.sbt file:
TwirlKeys.templateImports += "com.mongodb.DBObject"
This will add the previous import to all compiled templates. Note that it's for Play 2.3.x. If using 2.2.x, then you'd use the following instead:
templatesImport += "com.mongodb.DBObject"
I'd like to load 2 different input models (a .bpel and a .wsdl) in my main template of Acceleo.
I loaded the ecore metamodels for both bpel and wsdl and I'd like to be able to use something like this:
[comment encoding = UTF-8 /]
[module generate('http:///org/eclipse/bpel/model/bpel.ecore','http://www.eclipse.org/wsdl/2003/WSDL')/]
[import org::eclipse::acceleo::module::sample::files::processJavaFile /]
[template public generate(aProcess : Process, aDefinition : Definition)]
[comment #main /]
Process Name : [aProcess.name/]
Def Location : [aDefinition.location/]
[/template]
but when I run the acceleo template I get this error:
An internal error occurred during: "Launching Generate".
Could not find public template generate in module generate.
I think I have to modify the java launcher (generate.java) because right now it can't take 2 models as arguments. Do you know how?
Thanks!
** EDIT from Kellindil suggestions:
Just to know if I understood it right, before I get to modify stuff:
I'm trying to modify the Generate() constructor.
I changed it in:
//MODIFIED CODE
public Generate(URI modelURI, URI modelURI2, File targetFolder,
List<? extends Object> arguments) {
initialize(modelURI, targetFolder, arguments);
}
In the generic case, I can see it calls the AbstractAcceleoGenerator.initialize(URI, File, List>?>), shall I call it twice, once per each model? like:
initialize(modelURI, targetFolder, arguments);
initialize(modelURI2, targetFolder, arguments);
Then, to mimic in my Generate() constructor the code that is in the super-implementation:
//NON MODIFIED ACCELEO CODE
Map<String, String> AbstractAcceleoLauncher.generate(Monitor monitor) {
File target = getTargetFolder();
if (!target.exists() && !target.mkdirs()) {
throw new IOException("target directory " + target + " couldn't be created."); //$NON-NLS-1$ //$NON-NLS-2$
}
AcceleoService service = createAcceleoService();
String[] templateNames = getTemplateNames();
Map<String, String> result = new HashMap<String, String>();
for (int i = 0; i < templateNames.length; i++) {
result.putAll(service.doGenerate(getModule(), templateNames[i], getModel(), getArguments(),
target, monitor));
}
postGenerate(getModule().eResource().getResourceSet());
originalResources.clear();
return result;
}
what shall I do? Shall I try to mimic what this method is doing in my Generate() constructor after the initialize() calls?
What you wish to do is indeed possible with Acceleo, but it is not the "default" case that the generated launcher expects.
You'll have to mark the "generate" method of the generated java class as "#generated NOT" (or remove the "#generated" annotation from its javadoc altogether). In this method, what you need to do is mimic the behavior of the super-implementation (in AbstractAcceleoLauncher) does, loading two models instead of one and passing them on to AcceleoService#doGenerate.
In other words, you will need to look at the API Acceleo provides to generate code, and use it in the way that fits your need. Our generated java launcher and the AcceleoService class are there to provide an example that fits the general use case. Changing the behavior can be done by following these samples.
You should'nt need to modify the Generate.java class. By default, it should allow you to perform the code generation.
You need to create a launch config and provide the right arguments (process and definition) in this launch config, that's all.
I don't understand the 'client.xmi' URI that is the 1st argument of your module. It looks like it is your model file, if so remove it from the arguments, which must only contain your metamodels URIs.
There is a cookbook for adding globals to the twig templating engine, but it doesn't get into doing the same thing for the php engine. How would I do this?
So I might have something like:
# config.yml
someSortOfReferenceToThePHPEngineInstance:
calls:
- [ addGlobals, ["foo", "bar"] ]
- [ addGlobals, ["myService", "#myService"] ]
And then access those like:
// templateName.contentType.php
<?
echo $foo; // echos "bar"
echo $myService->myMethod($foo); // echos the result of modifying "bar" with "myMethod" method of "myService" service
I could not find any documention on this for the PHP engine...
What does work however is:
Config:
//config.yml
parameters:
hello: "YO!"
PHP Template:
// index.html.php
<?php
print $view->container->parameters['hello'];
This does not fit as nicely as the twig convention... Maybe there is better way - I have not debugged any further...
Here are a couple of options:
If you create a base controller that all others inherit from, you can override symfony's render function and add keys to the parameters argument, like:
public function render($view, array $parameters = array(), Response $response = null){
if(!array_key_exists("bar", $parameters){
$parameters["foo"] = $this->get("foo");
}
if(!array_key_exists("bar", $parameters){
$parameters["bar"] = $this->get("bar");
}
return parent::render($view, $parameters, $response);
}
This is the only way I see to modify the "global" variables "globally", though they'll not be available in any views rendered by controllers you don't create (of course, those'll likely be done in Twig anyway and you can use the normal twig means of adding functionality).
The PHP rendering engine has what're called "helpers", which you can access via array keys of $view, like:
$view["foo"]->doSomething();
We created a class for easily making services into helpers:
use Symfony\Component\Templating\Helper\Helper as BaseHelper;
class Helper extends BaseHelper{
protected $name;
public $service;
public function __construct($name, $service){
$this->name = $name;
$this->service = $service;
}
public function __get($name){
if(isset($this->service->$name)){
return $this->service->$name;
}
}
public function __call($name, $arguments){
if(method_exists($this->service, $name)){
return call_user_func_array(array($this->service,$name), $arguments);
}
}
public function getName(){
return $this->name;
}
}
Then in our configuration under the services we'd add:
helper.foo:
class: %helper.class%
arguments:
name: "foo"
helper: "#foo"
tags:
- { name: templating.helper, alias: foo }
This would theoretically be available then to any view files, even those with controllers you don't have control of.
I had a very same problem. For some reason this feature is only available for Twig templating with TwigBundle. Both Twig and PHP templating engines provide possibility to define global variables, but only Twig engine has configuration for that. For me the only real way to achieve that is something you proposed in the question post - to define method calls (and this is the way Twig globals are registered).
Problem is, that with DI extensions you can't access service definition from outside your extension, so you can't add these calls from your DI extension. The way for me was to do that with DI compiler pass.
But I'm also developer of ChillDevViewHelpersBundle and since I was facing this problem in most of my projects I decided to implement it there for common use and you can use 0.1.8 release for this feature.
I've created a web service that uses a generic type Response<TCode, TData> and so I'm ending up with elements like
ResponseOfResponseCodeUserData
ResponseOfResponseCodeArrayOfRightData
etc.
Functionally works just fine but I'm wondering if there's a way to name these particular elements?
EDIT:
Here's an example.
[return: XmlElement("AuthenticationResponse")]
[WebMethod]
public Response<ResponseCode, AuthenticationData> AuthenticateProcess(string ProcessName, string Password)
{
// ... Code ...
}
Still returns
<ResponseOfResponseCodeAuthenticationData (...) >
Any ideas?
It might help if you were to show some code.
Still, look at the [XmlElementAttribute] attribute, which allows you to specify the element name. If your issue is with return values, then you will need to use
[return: XmlRoot("ReturnElementName")]
[WebMethod]
public int MyWebMethod() { ... }