Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
  • 18 commits
  • 22 files changed
  • 0 comments
  • 5 contributors
14 documentation/commands/cmd-dependencies.txt
@@ -12,12 +12,12 @@
12 12 ~
13 13 ~ Description:
14 14 ~ ~~~~~~~~~~~~
15   -~ Compute resolve and retrieve project dependencies and install them either
16   -~ in lib/ directory for jar artifacts or in modules/ for Play modules artifacts.
  15 +~ Compute resolve and retrieve project dependencies and install them either in
  16 +~ lib/ directory for jar artifacts or in modules/ for Play modules artifacts.
17 17 ~
18   -~ By default the command will not delete unrecognized artifacts. Use the --sync option
19   -~ to keep both lib/ and modules/ directory synchronized with the dependencies management
20   -~ system.
  18 +~ By default the command will not delete unrecognized artifacts.
  19 +~ Use the --sync option to keep both lib/ and modules/ directory synchronized
  20 +~ with the dependencies management system.
21 21 ~
22 22 ~ Options:
23 23 ~ ~~~~~~~~
@@ -35,6 +35,10 @@
35 35 ~ Keep lib/ and modules/ directory synced.
36 36 ~ Delete unknow dependencies.
37 37 ~
  38 +~ --forceCopy:
  39 +~ Never create files pointing to the original folder, always copy the folder
  40 +~ instead.
  41 +~
38 42 ~ --%fwk_id:
39 43 ~ Use this ID to run the application (override the default framework ID)
40 44 ~
12 documentation/commands/cmd-version.txt
... ... @@ -0,0 +1,12 @@
  1 +~ Name:
  2 +~ ~~~~~
  3 +~ version -- Print the framework version
  4 +~
  5 +~ Synopsis:
  6 +~ ~~~~~~~~~
  7 +~ play version
  8 +~
  9 +~ Description:
  10 +~ ~~~~~~~~~~~~
  11 +~ Prints the version of the Play framework currently being used.
  12 +~
15 documentation/files/jquery.tools-1.2.5.toolbox.expose.min.js
... ... @@ -0,0 +1,15 @@
  1 +/*
  2 +
  3 + jQuery Tools 1.2.5 / Expose - Dim the lights
  4 +
  5 + NO COPYRIGHTS OR LICENSES. DO WHAT YOU LIKE.
  6 +
  7 + http://flowplayer.org/tools/toolbox/expose.html
  8 +
  9 + Since: Mar 2010
  10 + Date: Wed Sep 22 06:02:10 2010 +0000
  11 +*/
  12 +(function(b){function k(){if(b.browser.msie){var a=b(document).height(),d=b(window).height();return[window.innerWidth||document.documentElement.clientWidth||document.body.clientWidth,a-d<20?d:a]}return[b(document).width(),b(document).height()]}function h(a){if(a)return a.call(b.mask)}b.tools=b.tools||{version:"1.2.5"};var l;l=b.tools.expose={conf:{maskId:"exposeMask",loadSpeed:"slow",closeSpeed:"fast",closeOnClick:true,closeOnEsc:true,zIndex:9998,opacity:0.8,startOpacity:0,color:"#fff",onLoad:null,
  13 +onClose:null}};var c,i,e,g,j;b.mask={load:function(a,d){if(e)return this;if(typeof a=="string")a={color:a};a=a||g;g=a=b.extend(b.extend({},l.conf),a);c=b("#"+a.maskId);if(!c.length){c=b("<div/>").attr("id",a.maskId);b("body").append(c)}var m=k();c.css({position:"absolute",top:0,left:0,width:m[0],height:m[1],display:"none",opacity:a.startOpacity,zIndex:a.zIndex});a.color&&c.css("backgroundColor",a.color);if(h(a.onBeforeLoad)===false)return this;a.closeOnEsc&&b(document).bind("keydown.mask",function(f){f.keyCode==
  14 +27&&b.mask.close(f)});a.closeOnClick&&c.bind("click.mask",function(f){b.mask.close(f)});b(window).bind("resize.mask",function(){b.mask.fit()});if(d&&d.length){j=d.eq(0).css("zIndex");b.each(d,function(){var f=b(this);/relative|absolute|fixed/i.test(f.css("position"))||f.css("position","relative")});i=d.css({zIndex:Math.max(a.zIndex+1,j=="auto"?0:j)})}c.css({display:"block"}).fadeTo(a.loadSpeed,a.opacity,function(){b.mask.fit();h(a.onLoad);e="full"});e=true;return this},close:function(){if(e){if(h(g.onBeforeClose)===
  15 +false)return this;c.fadeOut(g.closeSpeed,function(){h(g.onClose);i&&i.css({zIndex:j});e=false});b(document).unbind("keydown.mask");c.unbind("click.mask");b(window).unbind("resize.mask")}return this},fit:function(){if(e){var a=k();c.css({width:a[0],height:a[1]})}},getMask:function(){return c},isLoaded:function(a){return a?e=="full":e},getConf:function(){return g},getExposed:function(){return i}};b.fn.mask=function(a){b.mask.load(a);return this};b.fn.expose=function(a){b.mask.load(a,this);return this}})(jQuery);
37 documentation/manual/guide1.textile
Source Rendered
@@ -196,15 +196,21 @@ bc. INFO ~ Connected to jdbc:h2:mem:play
196 196
197 197 h2. <a>Using a version control system to track changes</a>
198 198
199   -When you work on a project, it’s highly recommended to store your source code in a version control system (VCS). It allows you to revert to a previous version if a change breaks something, work with several people and give access to all the successive versions of the application. Of course you can use any VCS to store your project, but here we will use Bazaar as an example. "Bazaar":http://bazaar-vcs.org/ is a distributed source version control system.
  199 +When you work on a project, it’s highly recommended to store your source code in a version control system (VCS). It allows you to revert to a previous version if a change breaks something, work with several people and give access to all the successive versions of the application.
200 200
201   -Installing Bazaar is beyond the scope of this tutorial but it is very easy on any system. Once you have a working installation of Bazaar, go to the blog directory and init the application versioning by typing:
  201 +When storing a Play application in a VCS, it’s important to exclude the **tmp/**, **modules/**, **lib/**, **test-result/** and **logs/** directories.
202 202
203   -bc. $ bzr init
  203 +h3. <a>Bazaar</a>
204 204
205   -When storing a Play application in a VCS, it’s important to exclude the **tmp/** and **logs/** directories.
  205 +Here we will use Bazaar as an example. "Bazaar":http://bazaar-vcs.org/ is a distributed source version control system.
  206 +
  207 +Installing Bazaar is beyond the scope of this tutorial but it is very easy on any system. Once you have a working installation of Bazaar, go to the blog directory and init the application versioning by typing:
206 208
207   -bc. $ bzr ignore tmp
  209 +bc. $ bzr init
  210 +$ bzr ignore tmp
  211 +$ bzr ignore modules
  212 +$ bzr ignore lib
  213 +$ bzr ignore test-result
208 214 $ bzr ignore logs
209 215
210 216 Now we can commit our first blog engine version:
@@ -212,6 +218,27 @@ Now we can commit our first blog engine version:
212 218 bc. $ bzr add
213 219 $ bzr commit -m "YABE inital version"
214 220
  221 +h3. <a>Git</a>
  222 +
  223 +"Git":http://git-scm.com is another distributed version control system, see its documentation for more information.
  224 +
  225 +Create a git working repository at the application root directory:
  226 +
  227 +bc. $ git init
  228 +
  229 +Create a @.gitignore@ file containing the following content:
  230 +
  231 +bc. /tmp
  232 +/modules
  233 +/lib
  234 +/test-result
  235 +/logs
  236 +
  237 +Add the content of the application and commit it:
  238 +
  239 +bc. $ git add .
  240 +$ git commit -m "YABE initial version"
  241 +
215 242 Version 1 is committed and we now have a solid foundation for our project.
216 243
217 244 p(note). Go to the %(next)"A first iteration of the data model":guide2%.
12 documentation/manual/guide10.textile
Source Rendered
@@ -4,7 +4,7 @@ We’ve now finished the blog engine we wanted to create in this tutorial. Howev
4 4
5 5 Of course we’ve already written unit tests in order to test all the yabe model layer functionality. And it’s great as it will ensure that the blog engine’s core functionality is well tested. But a web application is not only about the ‘model’ part. We need to ensure that the web interface works as expected. That means testing the yabe blog engine’s controller layer. But we even need to test the UI itself, as for example, our JavaScript code.
6 6
7   -h2. <a>Testing the controller part</a>
  7 +h2. <a name="controller">Testing the controller part</a>
8 8
9 9 Play gives you a way to test directly the application’s controller part using JUnit. We call these tests **‘Functional tests’**. This is because we want to test the web application’s complete functionality.
10 10
@@ -53,7 +53,7 @@ Well, we could continue to test all the application functionalities this way, bu
53 53
54 54 These kinds of JUnit based **‘Functional tests’** are still useful, typically to test Web services returning non-HTML responses such as JSON or XML over HTTP.
55 55
56   -h2. <a>Writing Selenium tests</a>
  56 +h2. <a name="selenium">Writing Selenium tests</a>
57 57
58 58 "Selenium":http://seleniumhq.org is a testing tool specifically for testing web applications. The cool things here is that Selenium allows to run the test suite directly in any existing browser. As it does not use any ‘browser simulator’, you can be sure that you’re testing what your users will use.
59 59
@@ -176,11 +176,15 @@ clickAndWait('css=input[type=submit]')
176 176
177 177 And now run the test again, it should work.
178 178
179   -h2. <a>Measuring code coverage</a>
  179 +h2. <a name="cobertura">Measuring code coverage</a>
180 180
181 181 Of course we haven’t written all required test cases for the application. But it’s enough for this tutorial. Now in a real-world project, how can we know if we have written enough test cases? We need something called **‘code coverage’**.
182 182
183   -Play comes with a code coverage module based on the "Cobertura":http://cobertura.sourceforge.net/ tool. We need to enable this module only for test mode. So add this line to the **application.conf** file, and restart the application in test mode.
  183 +The ""Cobertura module":http://www.playframework.org/modules/cobertura generates code coverage reports using the "Cobertura":http://cobertura.sourceforge.net/ tool. Install the module using the @install@ command:
  184 +
  185 +bc. play install cobertura-{version}
  186 +
  187 +We need to enable this module only for test mode. So add this line to the @application.conf@ file, and restart the application in test mode.
184 188
185 189 bc. # Import the cobertura module in test mode
186 190 %test.module.cobertura=${play.path}/modules/cobertura
54 documentation/manual/i18n.textile
Source Rendered
@@ -68,7 +68,11 @@ bc. application.langs=fr,en
68 68 This property affects how the dates are rendered in the templates using the ${date.format()}.
69 69 It also set the default date format when binding a date parameter.
70 70
71   -h2. <a name="retrieve">Retrieve localized messages</a>
  71 +
  72 +h2(#retrieve). <a>Retrieve localized messages</a>
  73 +
  74 +
  75 +h3(#argument). Message arguments
72 76
73 77 From the application code, you can retrieve messages defined in message files. From Java, use the **play.i18n.Messages** object.
74 78
@@ -76,24 +80,66 @@ bc. public static void hello() {
76 80 renderText(Messages.get("hello"));
77 81 }
78 82
79   -We support message formatting through the standard Java formatting syntax. You can also define dynamic content in your messages:
  83 +We support message formatting through the standard @java.util.Formatter@ ‘Format string syntax’. You can also define dynamic content in your messages:
80 84
81 85 bc. hello=Hello %s!
82 86
83   -and
  87 +where @%s@ represents a message argument that will be output as a @String@. Message arguments are provided by additional (varargs) arguments to @Messages.get@:
84 88
85 89 bc. public static void hello(String user) {
86 90 renderText(Messages.get("hello", user));
87 91 }
88 92
  93 +h3(#template). Template output
  94 +
89 95 From a template you can use the special **&{…}** syntax to display localized messages:
90 96
91 97 bc. <h1>&{'hello'}</h1>
92 98
93   -or using dynamic content:
  99 +or using dynamic content in message arguments:
94 100
95 101 bc. <h1>&{'hello', params.user}</h1>
96 102
  103 +
  104 +h3(#arguments). Multiple arguments
  105 +
  106 +You can define multiple message arguments, such as this message which refers to two ‘decimal integer’ arguments:
  107 +
  108 +bc. guess=Please pick a number between %d and %d
  109 +
  110 +which you display by specifying the message arguments in the right order:
  111 +
  112 +bc. <p>&{'guess', low, high}</p>
  113 +
  114 +
  115 +h3(#indices). Argument indices
  116 +
  117 +You can also specify the message argument explicitly, to use a different order. For example, suppose a message in English has two parameters:
  118 +
  119 +bc. guess.characteristic=Guess %s’s %s.
  120 +
  121 +with message output like:
  122 +
  123 +bc. <p>&{'guess.characteristic', person.name, 'age'}</p>
  124 +
  125 +The French localisation has the two message in the opposite order, so in the French localisation we specify the argument indices:
  126 +
  127 +bc. guess.characteristic=Devinez %2$s de %1$s.
  128 +
  129 +where @%2$s@ outputs the **second** argument as a decimal integer.
  130 +
  131 +Finally, we want to localise the characteristic name ‘age’ as well, so we would change the output to use the message key @person.age@, and change the message definitions to:
  132 +
  133 +bc. guess.characteristic=Guess %s’s &{%s}.
  134 +person.age = age
  135 +
  136 +and
  137 +
  138 +bc. guess.characteristic=Devinez &{%2$s} de %1$s.
  139 +person.age = l’age
  140 +
  141 +where @&{%s}@ is itself a message look-up, with the argument value as the message key.
  142 +
97 143 p(note). **Continuing the discussion**
98 144
99 145 Next: %(next)"Cache":cache%.
46 documentation/manual/ide.textile
Source Rendered
@@ -5,7 +5,9 @@ Working with Play is easy. You don’t even need a sophisticated IDE as Play com
5 5 However, using a modern Java IDE provides cool productivity features like auto-completion, on-the-fly compilation, assisted refactoring and debugging. Play supports the "NetBeans":http://www.netbeans.org, "IntelliJ IDEA":http://www.jetbrains.com/idea/index.html and "Eclipse":http://www.eclipse.org platforms.
6 6
7 7
8   -h2(#eclipse). <a>Generate configuration files for Eclipse</a>
  8 +h2(#eclipse). <a>Eclipse</a>
  9 +
  10 +h3. Generate configuration
9 11
10 12 Play provides a command to simplify Eclipse configuration. To transform a Play application into a working Eclipse project, use the **eclipsify** command:
11 13
@@ -19,13 +21,18 @@ The **eclipsify** command generates several launchers for the application. The m
19 21
20 22 If you make any important changes to your application, such as changing the classpath, use **eclipsify** again to regenerate the configuration files.
21 23
22   -Additionally, an Eclipse plugin is available in your Play distribution, in the **support/eclipse/** directory. To install it, simply copy the JAR file you will find to your Eclipse installation’s **dropins** folder.
23   -
24 24 p(note). **Do not commit Eclipse configuration files when you work in a team!**
25 25
26 26 The generated configuration files contain absolute references to your framework installation. These are specific to your own installation. When you work in a team, each developer must keep his Eclipse configuration files private.
27 27
28   -h2(#netbeans). <a>Generate configuration files for NetBeans</a>
  28 +h3. Play Eclipse plug-in
  29 +
  30 +Additionally, Play comes with an Eclipse plug-in that provides editors for HTML view templates, @application.conf@ and the @routes@ file.
  31 +
  32 +To install, copy the JAR file from @$PLAY_HOME/support/eclipse@ to @$ECLIPSE_HOME/dropins@.
  33 +
  34 +
  35 +h2(#netbeans). <a>NetBeans</a>
29 36
30 37 Play provides a command to simplify NetBeans configuration. To transform an existing application to a valid NetBeans project, use the **netbeansify** command:
31 38
@@ -39,17 +46,29 @@ Use the standard *Run* button to start the application. When the application is
39 46
40 47 If you make any important change to your application such as changing the classpath, use **netbeansify** again to regenerate the configuration files.
41 48
42   -p(note). **Do not commit the nbproject/ directory when you work in a team!**
  49 +p(note). **Do not commit the @nbproject/@ directory when you work in a team!**
43 50
44 51 The generated configuration files contains absolute references to your framework installation. These are specific to your own installation. When you work in a team on the same application, each developer must keep his NetBeans configuration files private.
45 52
46   -h2(#intellij). <a>Generate configuration files for IntelliJ IDEA</a>
  53 +h2(#intellij). <a>IntelliJ IDEA</a>
47 54
48   -Play provides a command to simplify IntelliJ IDEA configuration. To transform an existing application to a valid IntelliJ IDEA module/project, use the **idealize** command:
  55 +Play provides a command to simplify IntelliJ IDEA configuration. To transform an existing application to a valid IntelliJ IDEA module, use the **idealize** command:
49 56
50 57 bc. # play idealize myApp
51 58
52   -Then you can just import the application in IntelliJ using the import module facility.
  59 +To create a single module project, do the following in IntelliJ IDEA.
  60 +
  61 +# On the *File* menu, select *New Project…*
  62 +# In the *New Project* wizard, select *Create project from scratch*.
  63 +# Click the *Next* button.
  64 +# Change the *Project files location* to the Play application directory.
  65 +# Set the *Project file format* to your preferred format.
  66 +# Uncheck *Create module* (since the module already exists).
  67 +# Click the *Finish* button.
  68 +# On the *File* menu, select *New Module…*
  69 +# On the *Add Module* wizard, select *Import existing module*.
  70 +# Under *Select IDEA module file*, select the @.iml@ file in the application directory.
  71 +# Click the *Finish* button.
53 72
54 73 !images/intellij!
55 74
@@ -62,14 +81,19 @@ The generated configuration files contains absolute references to your framework
62 81
63 82 h2(#textmate). <a>Textmate</a>
64 83
65   -Download and install the bundle provided for "Textmate":http://macromates.com/ to enable syntax coloring and auto-completion. The bundle also eases navigation between controllers and views.
  84 +Install the "Textmate":http://macromates.com/ bundle provided at @$PLAY_HOME/support/textmate.zip@ to enable syntax coloring and auto-completion. The bundle also eases navigation between controllers and views.
66 85
67 86 !images/editor!
68 87
69 88
70   -h2(#custom). <a>Manually configure your preferred editor</a>
  89 +h2(#vim). <a>Vim</a>
  90 +
  91 +Inspired by Textmate, the "snipMate":http://www.vim.org/scripts/script.php?script_id=2540 plug-in provides keyword auto-completion in "Vim":http://www.vim.org/. Play provides snippets files for HTML and Java: to use them, install snipMate and copy @$PLAY_HOME/support/vim/*.snippets@ to @~/.vim/snippets/@.
  92 +
  93 +
  94 +h2(#custom). <a>Custom configuration</a>
71 95
72   -As Play applications are standard Java applications, you don’t need a specific plug-in to work with your preferred editor. This, however, requires a little bit of knowledge of how Play works.
  96 +As Play applications are standard Java applications, you don’t need a specific plug-in to work with your preferred editor. This, however, requires a little bit of knowledge of how Play works.
73 97
74 98 h3. Classpath settings
75 99
2  documentation/manual/jobs.textile
Source Rendered
@@ -100,7 +100,7 @@ public class Bootstrap extends Job {
100 100
101 101 p(note). **Tip**
102 102
103   -We use the CRON expression parser from the "Quartz library":http://www.opensymphony.com/quartz/wikidocs/CronTriggers%20Tutorial.html.
  103 +We use the CRON expression parser from the "Quartz library":http://www.quartz-scheduler.org/docs/tutorials/crontrigger.html.
104 104
105 105 You don’t need to return a result. Even if you do it, the result will be lost.
106 106
4 documentation/manual/jpa.textile
Source Rendered
@@ -142,8 +142,8 @@ or even a part of:
142 142 bc. Post.find("title", "My first post").fetch();
143 143 Post.find("title like ?", "%hello%").fetch();
144 144 Post.find("author is null").fetch();
145   -Post.find("title like % and author is null", "%hello%").fetch();
146   -Post.find("title like % and author is null order by postDate", "%hello%").fetch();
  145 +Post.find("title like ? and author is null", "%hello%").fetch();
  146 +Post.find("title like ? and author is null order by postDate", "%hello%").fetch();
147 147
148 148 You can even specify only the **order by** statement:
149 149
30 documentation/manual/lambdaj.textile
Source Rendered
... ... @@ -1,18 +1,18 @@
1 1 h1. Some functional programming techniques
2 2
3   -The 1.1 release will allow "support of the Scala programming language":scala. One great thing (but not the only one) about Scala is that it is a mixed imperative -- functional language that allow to solve a lot of problem in a functional way.
  3 +Play supports the Scala programming language. One great thing (but not the only one) about Scala is that it is a mixed imperative/functional language that makes it possible to solve a lot of problems in a functional way.
4 4
5   -But now, what if you prefer keep using Java with play ? Could we bring some of the nice features of Scala to Java as well ? So let's me introduce the new "LambdaJ":http://code.google.com/p/lambdaj/ support in play.
  5 +But now, what if you prefer keep using Java with Play? Could we bring some of the nice features of Scala to Java as well ? This section describes Play’s "lambdaj":http://code.google.com/p/lambdaj/ support.
6 6
7   -The main purpose of lambdaj is to partially eliminate the burden to write (often nested and poorly readable) loops while iterating over collections. From the LambdaJ website:
  7 +The main purpose of lambdaj is to partially eliminate the burden to write (often nested and poorly readable) loops while iterating over collections. From the lambdaj website:
8 8
9 9 bq. How many times have you read or written the same two or three lines of code that frequently seem to go together, and even though they operate on different objects, feel like the same thing? And how often these repetitions involve some sort of collections iteration or more generically manipulation? These repetitions in the code is something that developers eventually learn to filter out and ignore when reading code, once they figure out where the interesting parts are placed. But even if the developers get used to it, it slows them down. Code like that is clearly written for computers to execute, not for developers to read.
10 10
11 11 lambdaj is a library that makes easier to address this issue by allowing to manipulate collections in a pseudo-functional and statically typed way. In our experience to iterate over collection, especially in nested loops, is often error prone and makes the code less readable. The purpose of this library is to alleviate these problems employing some functional programming techniques but without losing the static typing of java. We impose this last constraint to make refactoring easier and safer and allow the compiler to do its job.
12 12
13   -h2. <a>Let's use lambdaj</a>
  13 +h2. <a>Lets use lambdaj</a>
14 14
15   -We will start with a fresh application that allow to display a **Car** catalog. The **Car** model class will just be defined as:
  15 +We will start with a fresh application that displays a Car catalogue. The @Car@ model class is:
16 16
17 17 bc. package models;
18 18
@@ -41,18 +41,18 @@ public class Car extends Model {
41 41
42 42 }
43 43
44   -And let's write a simple action that retrieve all these cars:
  44 +And lets write a simple action that retrieve all these cars:
45 45
46 46 bc. public static index() {
47 47 List<Car> cars = Car.find().fetch();
48 48 render(cars);
49 49 }
50 50
51   -Now in the page it would be great to be able to order all these cars by brand. So we need to extract all the brand from the car list. Let's do it the lambdaj way:
  51 +Now in the page it would be great to be able to order all these cars by brand, so we need to extract the brands from the car list. Let’s do it the lambdaj way:
52 52
53 53 bc. List<String> brands = collect(cars, on(Car.class).brand);
54 54
55   -This line will iterate over all the retrieved cars, collect all the brands and feed them to the returned list. The very cool things is that we are able to express that stuff in a pure statically typed way.
  55 +This line will iterate over all the retrieved cars, collect all the brands and feed them to the returned list. The cool thing is that we are able to express that stuff in a pure statically-typed way.
56 56
57 57 Now we need to filter this list to remove brand duplication:
58 58
@@ -64,11 +64,11 @@ Very easy.
64 64
65 65 h2. <a>Batching method calls</a>
66 66
67   -We want to cound each time a Car has been viewed, and remember the last viewed time. As we already have the **viewed()** method that update the **Car** objects we just need to call this method on each retrieved car. Again, let's do it the lambdaj way:
  67 +We want to cound each time a car has been viewed, and remember the last viewed time. As we already have the @viewed()@ method that updates the @Car@ objects we just need to call this method on each retrieved car. Again, let’s do it the lambdaj way:
68 68
69 69 bc. forEach(cars).viewed();
70 70
71   -This line will iterate over each car object of the cars list and call the **viewed()** method on each.
  71 +This line will iterate over each car object of the cars list and call the @viewed()@ method on each.
72 72
73 73 And because we modified the state of the persistent objects, we need to save them. So rewrite it as:
74 74
@@ -76,9 +76,9 @@ bc. forEach(cars).viewed().save();
76 76
77 77 h2. <a>Using closures</a>
78 78
79   -Hugh? But Java doesn't have closure! Wait, lambdaj partially fill this lack by a feature that allow to define, in its traditional DSL style, first-class functions with free variables.
  79 +Huh? But Java doesn’t have closures! Wait, lambdaj partially fills this omission with a feature that allows you to define, in its traditional DSL style, first-class functions with free variables.
80 80
81   -Let's say we have a **PriceWatcher** utility able to fetch in real time the price of each car.
  81 +Let’s say we have a @PriceWatcher@ utility able to fetch in real time the price of each car.
82 82
83 83 bc. package models;
84 84
@@ -91,18 +91,18 @@ public class PriceWatcher {
91 91
92 92 }
93 93
94   -Because this data need to be **in real time** we don't want store it in the database. Before displaying the car list we need to create a PriceWatcher object and ask it to resolve the current price of each car. Again, let's do it the lambdaj way:
  94 +Because this data need to be **in real time** we don’t want store it in the database. Before displaying the car list we need to create a PriceWatcher object and ask it to resolve the current price of each car. Again, let’s do it the lambdaj way:
95 95
96 96 bc. PriceWatcher priceWatcher = new PriceWatcher();
97 97 Car.forEach(cars); {
98 98 of(priceWatcher).setPrice(var(Car.class));
99 99 }
100 100
101   -We define a function with a free variable, and then ask to the Car class to call them for each element of the cars list.
  101 +We define a function with a free variable, and then ask to the @Car@ class to call them for each element of the @cars@ list.
102 102
103 103 h2. <a>The final action code</a>
104 104
105   -Using all these good lambdaj stuff, we can finally write the **index** action in a very expressive fashion:
  105 +Using all these good lambdaj stuff, we can finally write the @index@ action in a very expressive fashion:
106 106
107 107 bc. public static void index() {
108 108 List<Car> cars = Car.find().fetch();
2  documentation/manual/overview.textile
Source Rendered
@@ -88,7 +88,7 @@ The Java Persistence API (JPA) is the cleanest object-relational mapping ORM) AP
88 88
89 89 Moreover if you use the provided **play.db.jpa.Model** superclass it will help make your code prettier. Have a look:
90 90
91   -bc. public void messages(int page) {
  91 +bc. public static void messages(int page) {
92 92 User connectedUser = User.find("byEmail", connected()).first();
93 93 List<Message> messages = Message.find(
94 94 "user = ? and read = false order by date desc",
10 documentation/manual/security.textile
Source Rendered
@@ -80,12 +80,18 @@ bc. public static destroyMyAccount() {
80 80
81 81 Will only work when called from a form including a proper authenticity token:
82 82
83   -bc. #{form @ destroyMyAccount()}
  83 +bc. <form method="post" action="/account/destroy">
84 84 #{authenticityToken /}
85 85 <input type="submit" value="destroy my account">
86 86 #{/form}
87 87
88   -You can of course add this as a before filter if you want to protect all actions of a hierarchy of controllers.
  88 +For POST requests, Play’s "form tag":tags#form automatically generates the authenticity token:
  89 +
  90 +bc. #{form @destroyMyAccount()}
  91 + <input type="submit" value="destroy my account">
  92 +#{/form}
  93 +
  94 +You can of course add the **checkAuthenticity()** method call as a "before filter":controllers#before if you want to protect all actions of a hierarchy of controllers.
89 95
90 96 "More on cross-site request forgery":http://en.wikipedia.org/wiki/Cross-site_request_forgery
91 97
35 documentation/manual/tags.textile
Source Rendered
@@ -20,13 +20,14 @@ If the action you try to call does not have any route able to invoke it using a
20 20
21 21 h2. <a name="authenticityToken">authenticityToken</a>
22 22
23   -Renders a hidden input field containing a generated token that you can use in any form. See the "Cross-Site Request Forgery":security#csrf section.
  23 +Renders a hidden input field containing a generated token that you can use in any form, to prevent "Cross-Site Request Forgery":security#csrf.
24 24
25 25 bc. #{authenticityToken /}
26 26
27 27 Rendered as:
28 28
29   -bc. <input type="hidden" name="authenticityToken" value="1c6d92fed96200347f06b7c5e1a3a28fa258ef7c">
  29 +bc. <input type="hidden" name="authenticityToken"
  30 + value="1c6d92fed96200347f06b7c5e1a3a28fa258ef7c">
30 31
31 32
32 33 h2. <a name="cache">cache</a>
@@ -195,41 +196,55 @@ Inserts a **form** tag. Play will guess the HTTP method from the route, with POS
195 196
196 197 Charset encoding is always **utf-8**.
197 198
198   -bc. #{form @Client.create(), method:'POST', id:'creationForm',
199   - enctype:'multipart/form-data' }
  199 +bc. #{form @Client.details(), method:'GET', id:'detailsForm'}
200 200 ...
201 201 #{/form}
202 202
203 203 Rendered as:
204 204
205   -bc. <form action="/client/create" id="creationForm" method="POST"
206   - accept-charset="utf-8" enctype="multipart/form-data">
  205 +bc. <form action="/client/details" id="detailsForm" method="GET"
  206 + accept-charset="utf-8">
207 207 ...
208 208 </form>
209 209
210 210 You can also specify a target entity as part of the action method:
211 211
212   -bc. #{form @Client.update(client.id)}
  212 +bc. #{form @Client.details(client.id)}
213 213 ...
214 214 #{/form}
215 215
216 216 The HTTP parameter name name is detected from what you declared in your action method.
217 217
218   -bc. public static void update(String clientId){
  218 +bc. public static void details(String clientId){
219 219 // ...
220 220 }
221 221
222 222 Play will create an action URL with clientId:
223 223
224   -bc. <form action="/client/update?clientId=3442" method="POST"
  224 +bc. <form action="/client/details?clientId=3442" method="GET"
  225 + accept-charset="utf-8">
  226 + ...
  227 +</form>
  228 +
  229 +The **form** tag also automatically includes an "authenticity token":#authenticityToken, for methods other than GET.
  230 +
  231 +bc. #{form @Client.create(), method:'POST', id:'creationForm',
  232 + enctype:'multipart/form-data' }
  233 + ...
  234 +#{/form}
  235 +
  236 +Rendered as:
  237 +
  238 +bc. <form action="/client/create" id="creationForm" method="POST"
225 239 accept-charset="utf-8" enctype="multipart/form-data">
  240 +<input type="hidden" name="authenticityToken"
  241 + value="1c6d92fed96200347f06b7c5e1a3a28fa258ef7c">
226 242 ...
227 243 </form>
228 244
229 245 If your form updates a resource on the server-side, you _should_ use the **POST** method. If your form is used to filter data and does not update your domain, you can use a GET. Please read about "idempotence":http://en.wikipedia.org/wiki/Idempotence. POST is not idempotent, whereas GET, PUT and DELETE are.
230 246
231 247
232   -
233 248 h2. <a name="get">get</a>
234 249
235 250 Retrieves a value defined with a **set** tag. You may use the get/set mechanism to exchange values between templates, layouts and sub-templates.
101 documentation/manual/validation.textile
Source Rendered
@@ -333,7 +333,7 @@ h2. <a name="builtin">Built-in validations</a>
333 333
334 334 The **play.data.validation** package contains several "built-in validations":validation-builtin that you can use on the **Validation** object or with annotations.
335 335
336   -h2. <a name="custom">Custom validation</a>
  336 +h2. <a name="custom">Custom validation using @CheckWith</a>
337 337
338 338 Can’t find the validator you need in the **play.data.validation** package? Write your own. You can use the generic **@CheckWith** annotation to bind your own **Check** implementation.
339 339
@@ -350,10 +350,107 @@ bc. public class User {
350 350 public boolean isSatisfied(Object user, Object password) {
351 351 return notMatchPreviousPasswords(password);
352 352 }
353   -
354 353 }
355 354 }
356 355
  356 +The default validation error message key is @validation.invalid@. To use a different key, call @Check.setMessage@ with a message key and message parameters.
  357 +
  358 +bc. static class MyPasswordCheck extends Check {
  359 +
  360 + public boolean isSatisfied(Object user, Object password) {
  361 + final Date lastUsed = dateLastUsed(password);
  362 + setMessage("validation.used", JavaExtensions.format(lastUsed));
  363 + return lastUsed == null;
  364 + }
  365 +}
  366 +
  367 +The message look up always has the field name as the first parameter, and your message parameters as subsequent parameters. So, for the example above, you could define the message like:
  368 +
  369 +bc. validation.used = &{%1$s} already used on date %2$s
  370 +user.password = Password
  371 +
  372 +where @&{%1$s}@ uses the message argument with index 1 (the field name) as the message key for another message look-up, and @%2$s@ is the second message argument (the formatted date).
  373 +
  374 +p(note). The message syntax - @%s@, @%s2$s@ and @&{…}@ - is explained in the section on how to "Retrieve localized messages":i18n#retrieve.
  375 +
  376 +
  377 +h2. <a name="customannotations">Custom annotations</a>
  378 +
  379 +You can also write your own annotation validations, which is more complex but makes your model code cleaner and allows you to introduce validator parameters.
  380 +
  381 +For example, suppose we want a less restrictive version of the "@URL":validation-builtin#url validation, so we can allow URLs with any scheme such as a @file://@ URL, and with a parameter that lets us specify exactly which schemes are allowed.
  382 +
  383 +First, we write a custom validation annotation, with a parameter for overriding the default message:
  384 +
  385 +bc. import net.sf.oval.configuration.annotation.Constraint;
  386 +import java.lang.annotation.*;
  387 +
  388 +@Retention(RetentionPolicy.RUNTIME)
  389 +@Target({ElementType.FIELD, ElementType.PARAMETER})
  390 +@Constraint(checkWith = URICheck.class)
  391 +public @interface URI {
  392 + String message() default URICheck.message;
  393 +}
  394 +
  395 +This annotation refers to an implementation of @net.sf.oval.configuration.annotation.AbstractAnnotationCheck@.
  396 +
  397 +bc. public class URICheck extends AbstractAnnotationCheck<URI> {
  398 +
  399 + /** Error message key. */
  400 + public final static String message = "validation.uri";
  401 +
  402 + /** URI schemes allowed by validation. */
  403 + private List<String> schemes;
  404 +
  405 + @Override
  406 + public void configure(URI uri) {
  407 + setMessage(uri.message());
  408 + this.schemes = Arrays.asList(uri.schemes());
  409 + }
  410 +
  411 + /**
  412 + * Add the URI schemes to the message variables so they can be included
  413 + * in the error message.
  414 + */
  415 + @Override
  416 + public Map<String, String> createMessageVariables() {
  417 + final Map<String, String> variables = new TreeMap<String, String>();
  418 + variables.put("2", JavaExtensions.join(schemes, ", "));
  419 + return variables;
  420 + }
  421 +
  422 + @Override
  423 + public boolean isSatisfied(Object validatedObject, Object value,
  424 + OValContext context, Validator validator) throws OValException {
  425 +
  426 + requireMessageVariablesRecreation();
  427 + try {
  428 + final java.net.URI uri = new java.net.URI(value.toString());
  429 + final boolean schemeValid = schemes.contains(uri.getScheme());
  430 + return schemes.size() == 0 || schemeValid;
  431 + } catch (URISyntaxException e) {
  432 + return false;
  433 + }
  434 + }
  435 +}
  436 +
  437 +The @isSatisfied@ method calls @requireMessageVariablesRecreation()@ to instruct OVal to call @createMessageVariables()@ before rendering the message. This returns an ordered map of variables that are passed to the message formatter. The map keys are not used; the @"2"@ in this example indicates the message parameter index. As before, the first parameter is the field name.
  438 +
  439 +To use this use the annotation on a model property.
  440 +
  441 +bc. public class User {
  442 +
  443 + @URI(message = "validation.uri.schemes", schemes = {"http", "https"})
  444 + public String profile;
  445 +}
  446 +
  447 +We can define the messages like this:
  448 +
  449 +bc. validation.uri = Not a valid URI
  450 +validation.uri.schemes = &{%1$s} is not a valid URI - allowed schemes are %2$s
  451 +
  452 +
  453 +
357 454 p(note). **Continuing the discussion**
358 455
359 456 The last layer of a Play application: %(next)"Domain object model":model%.
1  framework/dependencies.yml
@@ -50,6 +50,7 @@ require: &allDependencies
50 50 - org.hibernate.javax.persistence -> hibernate-jpa-2.0-api 1.0.0.Final
51 51 - org.javassist -> javassist 3.9.0.GA
52 52 - org.jboss.netty -> netty 3.2.4.Final
  53 + - org.postgresql -> postgresql 9.0
53 54 - org.slf4j -> slf4j-api 1.6.1
54 55 - org.slf4j -> slf4j-log4j12 1.6.1
55 56 - org.yaml -> snakeyaml 1.7
BIN  framework/lib/postgresql-9.0.jar
Binary file not shown
7 framework/pym/play/commands/deps.py
@@ -21,9 +21,14 @@ def execute(**kargs):
21 21 args = kargs.get("args")
22 22 play_env = kargs.get("env")
23 23
  24 + force = "false"
  25 + if args.count('--forceCopy') == 1:
  26 + args.remove('--forceCopy')
  27 + force = "true"
  28 +
24 29 classpath = app.getClasspath()
25 30
26   - add_options = ['-Dapplication.path=%s' % (app.path), '-Dframework.path=%s' % (play_env['basedir']), '-Dplay.id=%s' % play_env['id'], '-Dplay.version=%s' % play_env['version']]
  31 + add_options = ['-Dapplication.path=%s' % (app.path), '-Dframework.path=%s' % (play_env['basedir']), '-Dplay.id=%s' % play_env['id'], '-Dplay.version=%s' % play_env['version'], '-Dplay.forcedeps=%s' % (force)]
27 32 if args.count('--verbose'):
28 33 add_options.append('-Dverbose')
29 34 if args.count('--sync'):
14 framework/pym/play/commands/version.py
... ... @@ -0,0 +1,14 @@
  1 +
  2 +COMMANDS = ['version']
  3 +
  4 +HELP = {
  5 + 'version': 'Print the framework version'
  6 +}
  7 +
  8 +def execute(**kargs):
  9 + env = kargs.get("env")
  10 + showLogo = kargs.get("showLogo")
  11 +
  12 + # If we've shown the logo, then the version has already been printed
  13 + if not showLogo:
  14 + print env["version"]
7 framework/src/play/deps/DependenciesManager.java
@@ -193,6 +193,7 @@ public boolean problems() {
193 193 }
194 194
195 195 public File install(ArtifactDownloadReport artifact) throws Exception {
  196 + Boolean force = System.getProperty("play.forcedeps").equals("true");
196 197 try {
197 198 File from = artifact.getLocalFile();
198 199 if (!isPlayModule(artifact)) {
@@ -210,7 +211,11 @@ public File install(ArtifactDownloadReport artifact) throws Exception {
210 211 new File(application, "modules").mkdir();
211 212 Files.delete(to);
212 213 if (from.isDirectory()) {
213   - IO.writeContent(from.getAbsolutePath(), to);
  214 + if (force) {
  215 + IO.copyDirectory(from, to);
  216 + } else {
  217 + IO.writeContent(from.getAbsolutePath(), to);
  218 + }
214 219 System.out.println("~ \tmodules/" + to.getName() + " -> " + from.getAbsolutePath());
215 220 } else {
216 221 Files.unzip(from, to);
19 framework/src/play/libs/IO.java
@@ -346,4 +346,23 @@ public static void write(InputStream is, File f) {
346 346 }
347 347 }
348 348 }
  349 +
  350 + // If targetLocation does not exist, it will be created.
  351 + public static void copyDirectory(File source, File target) {
  352 + if (source.isDirectory()) {
  353 + if (!target.exists()) {
  354 + target.mkdir();
  355 + }
  356 + for (String child: source.list()) {
  357 + copyDirectory(new File(source, child), new File(target, child));
  358 + }
  359 + } else {
  360 + try {
  361 + write(new FileInputStream(source), new FileOutputStream(target));
  362 + } catch (IOException e) {
  363 + throw new UnexpectedException(e);
  364 + }
  365 + }
  366 + }
  367 +
349 368 }
4 modules/docviewer/public/playmanual/wiki.css
@@ -16,8 +16,8 @@
16 16 .wikistyle table td:first-child{padding-left:0!important;}
17 17 .wikistyle table td:last-child{padding-right:0!important;}
18 18 .wikistyle pre{margin:1em 0 2em 0!important;font-size:90%!important;background-color:#f8f8ff!important;border-left:4px solid #e0e0e0!important;padding:.5em!important;line-height:1.5em!important;color:#444!important;overflow:auto!important;}
19   -.wikistyle pre code{font-weight: normal; color: #444; display:block;padding:0!important;padding-left:10px!important;font-size:100%!important;background-color:#f8f8ff!important;border:none!important;}
20   -.wikistyle code{font-weight: bold; font-size:90%!important;color:#444!important;padding:0 .2em!important;}
  19 +.wikistyle code{font-family:Consolas,"Andale Mono",monospace;font-size:90%;color:#444;background-color:#f8f8ff;border:1px solid #e0e0e0;padding:0 0.2em!important;}
  20 +.wikistyle pre code{display:block;padding:0!important;padding-left:10px!important;font-size:100%!important;border:none!important;}
21 21 .wikistyle pre.console{margin:1em 0!important;font-size:90%!important;background-color:black!important;padding:.5em!important;line-height:1.5em!important;color:white!important;}
22 22 .wikistyle pre.console code{padding:0!important;font-size:100%!important;background-color:black!important;border:none!important;color:white!important;}
23 23 .wikistyle pre.console span{color:#888!important;}
2  play
@@ -53,6 +53,8 @@ try:
53 53 print r"~ | __/|_|\____|\__ (_)"
54 54 print r"~ |_| |__/ "
55 55 print r"~"
  56 + else:
  57 + sys.argv.remove("--silent")
56 58
57 59 play_version_file = os.path.join(play_env["basedir"], 'framework', 'src', 'play', 'version')
58 60 if not os.path.exists(play_version_file):

No commit comments for this range

Something went wrong with that request. Please try again.