Wednesday, March 19, 2008
Test in Grails
>>grails create unit-test
then Grails will ask you which Domain you want to test.for example type project
Then Grials will generate a class in directory: gwork\gira\workspace\test\integration
Write some code like this
class ProjectTests extends GroovyTestCase {
void testSort() {
def sortByAttribute={property ->
Project.findAll().sort{it."$property"}.title
}
def titles=sortByAttribute("title")
assert titles
assert titles.size()==3
assert titles[0]=="App1"
}
void setUp(){
new Project(title:'App1',description:'This is a test',createDate:new Date()).save()
new Project(title:'App2',description:'This is a test',createDate:new Date()).save()
new Project(title:'App3',description:'This is a test',createDate:new Date()).save()
}
void tearDown(){
Project.list()*.delete()
}
}
Then run command:
>>grails run-test
You will get the test reult.
Dynamic scaffod in Grails
• grails generate-views: Generates views for the specified domain class
• grails generate-controller: Generates a controller for the specified domain class
• grails generate-all: Generates both a controller and associated views
Hpw to change the default context of Grails server
>>grails run-app
I found the context of Grails server is still sparrow.Oh,how to change it.I found there is application.properties file in my project directory,I edit it like this
app.name=gria
Then the server's context change to gria.
Tuesday, March 18, 2008
The inList conatraint must follow blank constraint
priority(inList:["High","Low"])
then,when you open the page to edit or create this domain,you will find that the property can't be select by a HTML select box.So,you must follow another constraint blank and set it false.like this:
priority(inList:["High","Low"],blank:false)
Is it a bug?
Monday, March 17, 2008
Constraint in Grails
Name | Example | Description |
blank | title(blank:false) | Set to false if a string value cannot be blank |
email(email:true) | Set to true if the value must be an email address | |
inList | sex(inList:['Man','Woman']) | Value must be contain within the given list |
length | title(length:1..50) | Use a range value to restrict the length of string or range |
min | age(min:new Date() | Set the minimun value |
minLength | title(minLength:5) | Set the minimum length of a string or array property |
minSize | children(minSize:10) | Set the minimum size of a collection or number property |
matches | name(matches:/[a-zA-Z]/) | Matches the supplied regular expression |
max | age(max:new Date() | Set the maximum value |
maxLength | title(maxLength:5) | Set the maximum length of a string or array property |
maxSize | children(maxSize:10) | Set the maximum size of a collection or number property |
notEqual | name(notEqual:'Eric') | Must not equal the special value |
nullable | name | Set to false if property value cannot be null |
range | age(range:30..60) | Must occur within the special range |
size | children(size:5..15) | Use a range to restrict the size of a collection or number |
unique | name(unique:true) | Set to true if the property must be unique |
url | url(url:true) | Set to true if a string value is a url address |
HQL in Grail
/**find method*/
//following method will return a project object whose attribute is 'App1'
def project=Project.find('from Project p where p.title=?',['App1'])
/**findAll method*/
//following method will return all of project objects.
def projectList=Project.findAll('from Project')
//Grails also provide a elegant IN operator
def plist=Project.findAll('from Project')
def ilist=Issue.findAll("from Issue as b where b.project in (:p)", [p:plist])
/**executeQuery method*/
def plist=Project.executeQuery("select distinct p.title from Project p")
//it is possible to perform more-advanced queries using joins, aggregate functions, and subqueries.
And also we can some limit for query result number,for example:
def plist=Project.executeQuery("select distinct p.title from Project p",[max:10,offset:5])
)
Sunday, March 16, 2008
Dynamic Finders in Grails
This figure from the book:the definitive guid to grails.
Now I list the available Dynamic Finder method and example:
Expression Arguments Example
Between 2 Project.findByCreateDateBetween(today-10,today)
Equals 1 Project.findByTitleEquals('App1')
GreaterThan 1 Project.findByCreateDateGreaterThan(lastmonth)
GreaterThanOrEqual 1 Project.findByCreateDateGreaterThanOrEqual(lastmon)
IsNull 1 Project.findByTitleIsNull()
IsNotNull 1 Project.findByTitleIsNotNull()
LessThan 1 Project.findByCreateDateLessThan(lastmonth)
LessThanOrEqual 1 Project.findByCreateDateLessThanOrEqual(lastmonth)
Like 1 Project.findByTitleLike('App1%')
NotEqual 1 Project.findByTitleNotEqual('App1')
All of findBy* method only return that fisrt be found object.
There are two cousins method findAllBy* and countBy*.
findAllBy* :return a list that is matched the query logic
countBy* :return a number that is matched result total.
Data operator CRUD in Grails
/**Create data*/
//create a project
def project1=new Project(title:'Project1',description:'Hello world!',createDate:new Date())
//now we have the project object.we will save it.
project1.save()
//as you can see ,we never define the method save().but we can use it,because Grails auto //generate it in runtime for us.when we call this method,the project information will be saved in database table,it's cool.
//Now we'll define issue object
def issue1=new Issue(summary:'Issue1',description:'This is a issue',createDate:new Date(),project:project1)
//we created the issue object,but the different in this place.we have a parameter project:project1 ,because the project and issue has a one-many relationship,so we need point there whcih Project that this Issue belong to.
/**read*/
after we create the object information.we usually need get these data.how to do that?the answer is get(id) and exists(id) method.
//get Project by ID
def p1=Project.get(1)//we will use id parameter get a project,which id is 1
def boolean=Project.exist(1)//the exists method will return ture if it's exists.
The exists method, reads better and is arguably more performant than get method.
if(Project.get(1)){
def p=Project.get(1) //bad performant,because you called twice get method
}
if(Project.exists(1)){
def p=Project.get(1) //better performant
}
/**update Object*/
//update object is easy
def p=Project.get(1)
p.title='New title'
p.save()
/**delete Object*/
def p=Project.get(1)
p.delete()
/**sort,listing,and counting*/
GORM provides a list method, which takes a number of named arguments such as max, offset,
sort, and order to customize the results, the definitions of which are listed here:
• max: The maximum number of instances to return
• offset: The offset relative to 0 of the first result to return
• sort: The property name to sort by
• order: The order of the results, either asc or desc for ascending and descending order respectively
//demonstrate
//get all objecta
def allProject=Project.list()
//get top 10 objects
def topten=Project.list(max:10)
//sort objects
def sortingProject=Project.list(max:10,sort:'createDate')
//sort and order
def sortingProject2=Project.list(max:10,sort:'createDate',order:'desc')
//order by column name
def allBycreateDate=Project.listOrderByCreateDate()
Note:the listByCreateDate is auto generate by Grails.it's a role,that means listOrderBy+property'name
Friday, March 14, 2008
How to create the Domain-class of Grails--Sparrow begin
But before the beginning of our process,let me introduce the requirement of our Sparrow.For facilitate,I will illustrate the requirement by a UML diagram.
As you can see, our project will include three functions.
1)create a project.
2)create issues for this project.
3)create issues type for existing issues.
Following is the Domain Class Model :
Now,let's begin.In Grails,it use the Hibernate for ROM ,but as the same time Grails extend the Hibernate,we call this extension "GOM".It's also based on "Convention Role",so we only need create a POJO class by Groovy language.In our project there are three domain class we'll create.Grail provide us a easy to used tool.To do this,just open the command line.type:
>>grails create-domain-class
then enter the class name ,for example :project.
Grails will auto generate a class with capitalize letter.like this:
class Project {
static hasMany=[issues:Issue]//one project can has many issues
String title
String description
Date createDate
}
Note:the hasMany is a key word of Grails,that means one project can has many issues.so Issue class will have a belongsTo attribute.like this:
class Issue {
static belongsTo=Project
Project project
String summary
String description
Date createDate
}Because of we have create our database(postgreSql).now we run our application.
>>grails run-app
You'll see that grails auto create the database table for these two domain class.
As you can see Grails auto create two field "id" and "version".it's a convention,also id is a default key and it's request.And also,may be you have saw that the property field "createDate" in database table is create_date,because the createDate field has a capitalize letter "D",so it replace by "_d".yes it's also a convention.
Wednesday, March 12, 2008
How to config Grails's datasource
• DB2 7.1, 7.2, 8.1
• HSQLDB
• HypersonicSQL 1.61, 1.7.0, 1.7.2, 1.8
• Microsoft SQL Server 2000
• MySQL 3.23, 4.0, 4.1, 5.0
• Oracle 8i, 9i, 10g
• PostgreSQL 7.1.2, 7.2, 7.3, 7.4, 8.0, 8.1
• SAP DB 7.3
• Sybase 12.5 (JConnect 5.5)
• Timesten 5.1
Let me assume that we use postgresql.Then we should download the JDBC driver from postgresql homepage,and copy it into the directoty lib($App_home$/lib).Then edit the file DataSource.groovy in directory conf.like this(I add some comments for every section):
dataSource {
pooled = true //which allows you to configure whether to use a connection pool or not
driverClassName = "org.postgresql.Driver" //the database driver class name
username = "postgres" //database name
password = "123456" //database password
}
//It is the configurate for hibernate,you can change it fit your want.
hibernate {
cache.use_second_level_cache=true
cache.use_query_cache=true
cache.provider_class='org.hibernate.cache.EhCacheProvider'
}
// environment specific settings,there are three database for our application.
//sparrow_dev:for development phase.
//sparrow_test:for test phase.
//sparrow_pro:for product phase.
environments {
development {
dataSource {
dbCreate = "update" // one of 'create', 'create-drop','update'
url = "jdbc:postgresql://127.0.0.1:5432/sparrow_dev"
}
}
test {
dataSource {
dbCreate = "update"
url = "jdbc:postgresql://127.0.0.1:5432/sparrow_test"
}
}
production {
dataSource {
dbCreate = "update"
url = "jdbc:postgresql://127.0.0.1:5432/sparrow_pro"
}
}
}
Now,you can run you application under a database model.congratulation.
Monday, March 10, 2008
What the Grails application directory contains?
grails-app:This directory containing core Grails artifacts
+conf: Contains configuration elements,such as DataSource.groovy,UrlMappings.groovy
+controllers :Controllers that handle requests
+domain:Contains domain object class.
+i18n:Contains internationalization message bundles.
+services:Services that encapsulate business logic
+taglib:Dynamic tag libraries
+views:Groovy server page or JSP.
+layouts:the GSP or JSP layouts powered by sitemesh.
grails-test:application unit tests.
hibernate:optional Hibernate configuration.
lib:java lib jar archives
spring:optional Spring configuration.
src:Include other Groovy or Java sources.
+java:Further Java sources for compilation.
+groovy:Further Groovy sources for compilation.
web-app:web application resources (CSS, JavaScript, etc.)
How to change the default servlet version of Grails
1)passs argument -Dservlet.version,for example:
>>grails -Dservlet.version=2.5 run-app
2)Or we can directly edit the Init.groovy file in directory Grails_Home/scripts.find the key "servletVersion" and change its default value from 2.4 to others what you want,for example:
servletVersion = System.getProperty("servlet.version") ? System.getProperty("servlet.version") : "2.5"
How to display database comments of table's columns
select * from user_col_comments where table_name='table name'
as same principle.we can show the table's comments:
select * from user_tab_comments where table_name='table name'
How to change the default port number of Grails server
1) when you do run-app,add command -Dserver.port=9090.for example
>>grails -Dserver.port=9090 run-app
2) if you want to change the port to 9090 as default value,then Edit the file GRAILS_HOME/scripts/Init.groovy and change the phrase 8080 to another port number like 9090 .
A good IDE for develop dynamic language.
We decide to find a easy to use and free license IDE for our team.After a several of days,we found a good tools open komodo .It's support a lot of existing dynamic language.such as python,ruby,lisp and several of dynamic language such as django. etc....Also it's support customizable feature ,such as customize command,customize menu etc.. We'll use this IDE in our Sparrow application.
Sunday, March 9, 2008
The Controllers in Grails framework
So let's create our first controller for our Sparrow.let's assume we want a page to say hello for everyone who access our app.The requirement is simple,it's just print a "hello world "(Yes,always hello world).It's easy to do this in Grails.We need a controllers,type following command:
>>cd sparrow
>>grails create-controller
Grails will ask you what's controller name.pls type hello
After a minute,Grails will create a HelloController.groovy in path /sparrow/grails-app/controllers/
then open it and find the index method.Note every methods in this groovy file are action .the index method is the default behavior when you navigate following url:http://127.0.0.1:8080/sparrow/hello/
We will modify this method like this :
def index = {
render "Hello World!"
}
When you navigate http://127.0.0.1:8080/sparrow/hello/
you'll see a page with "Hello World!" string.Note this file is a standard groovy file.index method is a default method as I said at above.the render also a keyword.We can assume it like Response object's write method in Java servelt.
If you want define other action in hello controller.just define a new method in the file HelloController.groovy.for example.I want to show a good bye information.Just add following method :
def saygoodbye={
render "Good Bye!"
}
then navigate to following url: http://127.0.0.1:8080/sparrow/hello/saygoodbye.
Note:in this url,
the http://127.0.0.1:8080/ is domain and port.
the saprrow is context path.
the hello is controler name.
the saygoodbye is action name.
In future section we'll discuss it detail.
My friend get a new job
In the end, I wish she have a good luck beginning.God bless her.
Getting start with Grails for our Sparrow app
As you can see,our app's name is Sparrow,then how to create it,follow me.
open your command line.type
>>make dir learnapp
>>cd learnapp
>>grails create-app
after this,grails will ask your what's name your project.we'll tyep
>>sparrow
then grails will generate all of you need to start an application.Note,Grails is a role framework.so you will see somes like domain,controllors,tlb folder etc.in the future,we'll create relative file to fit our application.for now,we only need a blamk frame,but it's a runable application.we'll start our first grails app.type following command:
>>cd sparrow
>>grails run-app
You will see some run time information in console.then open your favorite browser then type:
http://127.0.0.1:8080/sparrow/
you will see a welcom page like this:
Grails used Jetty as its default servlet container.In the future,may be you want to move it to other containers such as tomcat .or some appservers such as JBoss.You can just type grails war .Grails will generate a war file for you,then you can make your own deployment in others environment.
Now.through only a few commmand you create a runable application.It's really cool.In the following section,we'll discuss how to add our first domain model project.we'll add some useful feature for our application and introduce what grails's domain model is as same time.
Grails Command-Line Help
>grails
help:
Usage: grails [target]
Targets:
"create-app" - Create a new grails app
"create-controller" - Create a new controller
"create-service" - Create a new service
"create-domain-class" - Create a new domain class
"create-taglib" - Create a new tag library class
"create-test-suite" - Create a new test suite
"create-job" - Create a quartz scheduled job
"generate-controller" - Generates a controller from a domain class
"generate-views" - Generates the views from a domain class
"generate-all" - Generates all artifacts from a domain class
"test-app" - Run current app's unit tests
"run-app" - Run the application locally and wait
"create-webtest" - Create the functional test layout
"run-webtest" - Run the functional tests for a running app
"shell" - Opens the Grails interactive command line shell
"console" - Opens the Grails interactive swing console
"war" - Create a deployable Web Application Archive (WAR)
While you run these command.you will note at this point that Grails uses the Apache Ant.
Domain model for Sparrow
Today we create the domain model for Sparrow system.For now,it's only a draft version.
Also you can find the soure file from here
Start new project Sparrow
Friday, March 7, 2008
Lock step iteration in Python
>>a=(1,2,3)
>>b=(4,5,6)
>>for i in map(None,a,b): print i
The result is :
(1, 4)
(2, 5)
(3, 6)
Wow.It's cool ,right.now assume the length of each list is different.for example,we set them like this
>>a=(1,2,3)
>>b=(4,5,6,7,8)
The result is:
(1, 4)
(2, 5)
(3, 6)
(None, 7)
(None, 8)
As you can see,now we have two extra small list with the element None.Yes,Python auto append a None type in last list,because it is the first parameter in the map function.Until now,it looks good ,right ?we only write one line code ,then map function do two times loop.But it's not elegant.because :
- It is non-obvious to programmers without a functional
programming background.
- The use of the magic `None' first argument is non-obvious.
- It has arbitrary, often unintended, and inflexible semantics
when the lists are not of the same length: the shorter sequences
are padded with `None'.
Fortunately,we have another function,zip.yes it's good to use.let me give a example:
>>a=(1,2,3)
>>b=(4,5,6,7,8)
>>zip(a,b)
The result is:
(1, 4)
(2, 5)
(3, 6)
Yes,as you can see,no other two redundant list.it's more clearly.of course,if you think that
other twos is useful,you can also use the map function.But now we have another choose.