SlideShare a Scribd company logo
1 of 55
Download to read offline
GRAILS
N00B TO NINJA IN 3 HOURS
[INTRODUCTION WORKSHOP]
/BrianJohnsen @brianjohnsendk
WHOAMI
(whocares)
Brian Johnsen, GennemtænktITA/S
12+ years Java
Groovy&Grails since 2008
Your FriendlyNeighborhood GR8Conf Organizer
READY?
1. Copygrails installto disk and unzip
2. Add to path
3. Voilá
$grails-version
Grailsversion:2.3.8
FULL STACK FRAMEWORK
ORM (GORM)
Powerfulview technology(GSP) and easyTagLib creation
DependencyInjection and on-the-flyreloading(Spring)
Internationalization support(i18n)
Runtime Container (Tomcat)
TestingFramework (Spock)
Plugin System
Command line tools
Build Tool(Gant/Gradle)
and abunch of other stuff…
ARCHITECTURE
PHILOSOPHY
LETS GET STARTED!
DEMO TIME!
CLI and create app demo
YOUR TURN!
Itjustgoes on...
CLI
Tryitout
$grailshelp
CREATE THE APP
$grailscreate-appgrailsdemo
$cdgrailsdemo
$grailsrun-app
|Serverrunning.Browsetohttp://localhost:8080/grailsdemo
Openyourfavoritebrowserandcheckitout!
RUNNING APPLICATION!
ANATOMY
ANATOMY
grails-app-TopleveldirectoryforGroovysources
conf-Configurationsources.
controllers-Webcontrollers-TheCinMVC.
domain-Theapplicationdomain-TheMinMVC.
i18n-Supportforinternationalization(i18n).
migrations-Databasemigration.
services-Theservicelayer.
taglib-Taglibraries.
utils-Grailsspecificutilities.
views-GroovyServerPages-TheVinMVC.
scripts-Gantscripts.
src-Supportingsources
groovy-OtherGroovysources
java-OtherJavasources
test-Unitandintegrationtests.
web-app-JavaScriptandCSS.
wrapper-Wrapper.
DON'T LET FRIENDS FORK
Open:
/grails-app/conf/BuildConfig.groovy
Change:
grails.project.fork=[
//configuresettingsforcompilationJVM,notethatifyoualtertheGroovyversion
...
...
//configuresettingsfortheConsoleUIJVM
console:[maxMemory:768,minMemory:64,debug:false,maxPerm:256]
]
To:
grails.project.fork=false
DOMAIN
/grails-app/domain/
The M in MVC
Gets mapped to the database
Dynamic finders FTW -> ForgetSQL
constraintsenforce database consistency
Defines scaffolded views
DOMAIN DEMO!
MY FIRST DOMAIN CLASS
$grailscreate-domain-classPerson
//grails-app/domain/Person.groovy
classPerson{
Stringname
Integerage
staticconstraints={
nameblank:false
agemin: 0
}
}
SAVE AND VALIDATE
constraintsare enforced on save()and validate()
defperson=newPerson(age:42,name:'DouglasAdams')
person.validate()//onlychecksconstraints
person.save()//savestheinstancetothedatabase
WHOLE LOTTA DYNAMIC GOIN’ ON
Finders are dynamicallycreated for allproperties
defeverybody=Person.list()
deftheOne=Person.findByNameAndAge('Brian',41)
defcaseInsensitive=Person.findAllByNameIlike('graeme')
defunderAge=Person.findAllByAgeLessThan(18)
defseniors=Person.findAllByAgeGreaterThanEquals(65)
deforderedByName=Person.listOrderByName()
TESTING
/test/unit/
Spock Specifications
Deeplyintegrated in Grails
When creatinganyartifactacorrespondingtestis created
CreatingPersonalso creates aPersonSpec
TEST DEMO!
SPOCK TEST DEFAULT
//test/unit/grailsdemo/PersonSpec.groovy
@TestFor(Person)
classPersonSpecextendsSpecification{
defsetup(){
}
defcleanup(){
}
void"testsomething"(){
}
}
WRITE A TEST!
"age should be more than 0"
COULD LOOK LIKE THIS
//test/unit/grailsdemo/PersonSpec.groovy
@TestFor(Person)
classPersonSpecextendsSpecification{
void"ageshouldbemorethan0"(){
when:
defperson= newPerson(age:age,name:'joe')
then:
person.validate()==valid
where:
age |valid
0 | true
41 | true
-1 | false
}
}
TESTING
- THE EASIEST WAY TO EXECUTE CODE!
Playwith dynamic finders
TAKING CONTROL
/grails-app/controllers/
The C in MVC
Databinding
Redirectingrequests
Delegatingto business logic
HIM AGAIN!
Scaffolded UI Demo
SCAFFOLDED CONTROLLER
//grails-app/controllers/grailsdemo/PersonController.groovy
classPersonController{
staticscaffold=true
}
Fire itup and give itaspin
$grailsrun-app
STOP THE PRESSES
IT'S BACKED BY A REAL DATABASE!
Browse to http://localhost:8080/demo/dbconsole
WANT SOME REST?
AUTOMATIC CONTENT NEGOTIATION
http://localhost:8080/demo/person/show/1.json
{
class:"grailsdemo.Person",
id:1,
age:41,
name:"Brian"
}
http://localhost:8080/demo/person/show/1.xml
<personid="1">
<age>41</age>
<name>Brian</name>
</person>
BACK ON TRACK
GENERATE CONTROLLER
$grailsgenerate-controllergrailsdemo.Person
ALL THAT CODE
Open:
grails-app/controllers/grailsdemo/PersonController.groovy
SOMETHING LIKE THIS?
//grails-app/controllers/grailsdemo/PersonController.groovy
@Transactional(readOnly=true)
classPersonController{
staticallowedMethods=[save:"POST",update:"PUT",delete:"DELETE"]
defindex(Integermax){
params.max=Math.min(max?:10,100)
respondPerson.list(params),model:[personInstanceCount:Person.count()]
}
defshow(PersonpersonInstance){
respondpersonInstance
}
...
...
}
TESTING CONTROLLERS
Run:
$grailstest-appunit:PersonController
Whathappens?
Open:
/test/unit/grailsdemo/PersonControllerSpec.groovy
TESTING CONTROLLERS
Change:
defpopulateValidParams(params){
assertparams!=null
//TODO:Populatevalidpropertieslike...
//params["name"]='someValidName'
}
To:
defpopulateValidParams(params){
assertparams!=null
params["name"]='joe'//orwhatever
params["age"]=42//orwhatever
}
MORE TESTING
Do we have the time?
VIEWS
/grails-app/views/
The Vin MVC
GSP
GENERATE VIEWS
$grailsgenerate-allgrailsdemo.Person
THE INDEX VIEW
Open:
grails-app/views/person/index.gsp
THE SHOW VIEW
Open:
grails-app/views/person/show.gsp
CREATE AND EDIT VIEWS - TEMPLATES
Open:
grails-app/views/person/create.gsp
grails-app/views/person/edit.gsp
grails-app/views/person/_form.gsp
SERVICES
/grails-app/services/
Should contain allbusiness logic
Transactional
Injected into anyartefact
CREATE THE SERVICE
$grailscreate-servicePerson
Open:
grails-app/services/demo/PersonService.groovy
UPDATE THE SERVICE
@Transactional
classPersonService{
defgetAll(){
Person.list()
}
}
UPDATE THE CONTROLLER
classPersonController{
PersonServicepersonService//injectedbygrails
defindex(Integermax){
respondpersonService.getAll()
}
...
}
LETS ADD A NEW FEATURE
Person should have country
//grails-app/domain/Person.groovy
classPerson{
Stringname
Integerage
Stringcountry
staticconstraints={
nameblank:false
age min:0
countrynullable:true
}
}
UPDATE THE TESTS
Open:
test/unit/grailsdemo/PersonSpec.groovy
UPDATE THE VIEW
Open:
grails-app/views/person/index.gsp
and:
grails-app/views/person/_form.gsp
ANOTHER NEW FEATURE
Creatinganew person should send you to the listview (index)
Open:
grails-app/controllers/grailsdemo/PersonController.groovy
PLUGINS
https://grails.org/plugins/
Build TestData
Searchable
Quartz
SpringSecurity
MongoDB
and itgoes on and on...

More Related Content

What's hot

When Will Drupal Die?
When Will Drupal Die?When Will Drupal Die?
When Will Drupal Die?
chrisshattuck
 

What's hot (20)

Beyond QA
Beyond QABeyond QA
Beyond QA
 
This Week in Neo4j - 6th October 2018
This Week in Neo4j - 6th October 2018This Week in Neo4j - 6th October 2018
This Week in Neo4j - 6th October 2018
 
iTHome Gopher Day 2017: What can Golang do? (Using project 52 as examples)
iTHome Gopher Day 2017: What can Golang do?  (Using project 52 as examples)iTHome Gopher Day 2017: What can Golang do?  (Using project 52 as examples)
iTHome Gopher Day 2017: What can Golang do? (Using project 52 as examples)
 
Creating mobile apps the web developer way
Creating mobile apps the web developer wayCreating mobile apps the web developer way
Creating mobile apps the web developer way
 
iThome Chatbot Day: 透過 Golang 無痛建置機器學習聊天機器人
iThome Chatbot Day: 透過 Golang 無痛建置機器學習聊天機器人iThome Chatbot Day: 透過 Golang 無痛建置機器學習聊天機器人
iThome Chatbot Day: 透過 Golang 無痛建置機器學習聊天機器人
 
賣 K8s 的人不敢告訴你的事 (Secrets that K8s vendors won't tell you)
賣 K8s 的人不敢告訴你的事 (Secrets that K8s vendors won't tell you)賣 K8s 的人不敢告訴你的事 (Secrets that K8s vendors won't tell you)
賣 K8s 的人不敢告訴你的事 (Secrets that K8s vendors won't tell you)
 
Continuous integration with Docker
Continuous integration with DockerContinuous integration with Docker
Continuous integration with Docker
 
When Will Drupal Die?
When Will Drupal Die?When Will Drupal Die?
When Will Drupal Die?
 
a pattern for PWA, PRPL
a pattern for PWA, PRPLa pattern for PWA, PRPL
a pattern for PWA, PRPL
 
Hashitalks 2021 Infrastructure Drift & Driftctl
Hashitalks 2021 Infrastructure Drift & Driftctl Hashitalks 2021 Infrastructure Drift & Driftctl
Hashitalks 2021 Infrastructure Drift & Driftctl
 
Serving ML easily with FastAPI
Serving ML easily with FastAPIServing ML easily with FastAPI
Serving ML easily with FastAPI
 
Magento, beginning to end
Magento, beginning to endMagento, beginning to end
Magento, beginning to end
 
plone.api
plone.apiplone.api
plone.api
 
Grails at DMC Digital
Grails at DMC DigitalGrails at DMC Digital
Grails at DMC Digital
 
GitOps is IaC done right
GitOps is IaC done rightGitOps is IaC done right
GitOps is IaC done right
 
Pain Driven Development by Alexandr Sugak
Pain Driven Development by Alexandr SugakPain Driven Development by Alexandr Sugak
Pain Driven Development by Alexandr Sugak
 
用 Go 語言實戰 Push Notification 服務
用 Go 語言實戰 Push Notification 服務用 Go 語言實戰 Push Notification 服務
用 Go 語言實戰 Push Notification 服務
 
Starting a Software Developer Career
Starting a Software Developer CareerStarting a Software Developer Career
Starting a Software Developer Career
 
如何透過 Golang 與 Heroku 來一鍵部署 臉書機器人與 Line Bot
如何透過 Golang 與 Heroku 來一鍵部署 臉書機器人與 Line Bot如何透過 Golang 與 Heroku 來一鍵部署 臉書機器人與 Line Bot
如何透過 Golang 與 Heroku 來一鍵部署 臉書機器人與 Line Bot
 
GitLab Frontend and VueJS at GitLab
GitLab Frontend and VueJS at GitLabGitLab Frontend and VueJS at GitLab
GitLab Frontend and VueJS at GitLab
 

Similar to The Grails introduction workshop

Ratpack - Classy and Compact Groovy Web Apps
Ratpack - Classy and Compact Groovy Web AppsRatpack - Classy and Compact Groovy Web Apps
Ratpack - Classy and Compact Groovy Web Apps
James Williams
 
Spring boot 3g
Spring boot 3gSpring boot 3g
Spring boot 3g
vasya10
 
Django: Beyond Basics
Django: Beyond BasicsDjango: Beyond Basics
Django: Beyond Basics
arunvr
 

Similar to The Grails introduction workshop (20)

Grails @ Java User Group Silicon Valley
Grails @ Java User Group Silicon ValleyGrails @ Java User Group Silicon Valley
Grails @ Java User Group Silicon Valley
 
Groovy & Grails
Groovy & GrailsGroovy & Grails
Groovy & Grails
 
Use Groovy&Grails in your spring boot projects
Use Groovy&Grails in your spring boot projectsUse Groovy&Grails in your spring boot projects
Use Groovy&Grails in your spring boot projects
 
Whats New In Groovy 1.6?
Whats New In Groovy 1.6?Whats New In Groovy 1.6?
Whats New In Groovy 1.6?
 
第1回名古屋Grails/Groogy勉強会「Grailsを始めてみよう!」
第1回名古屋Grails/Groogy勉強会「Grailsを始めてみよう!」第1回名古屋Grails/Groogy勉強会「Grailsを始めてみよう!」
第1回名古屋Grails/Groogy勉強会「Grailsを始めてみよう!」
 
Grails beginners workshop
Grails beginners workshopGrails beginners workshop
Grails beginners workshop
 
Baiscs of OpenGL
Baiscs of OpenGLBaiscs of OpenGL
Baiscs of OpenGL
 
Spring-batch Groovy y Gradle
Spring-batch Groovy y GradleSpring-batch Groovy y Gradle
Spring-batch Groovy y Gradle
 
Ratpack - Classy and Compact Groovy Web Apps
Ratpack - Classy and Compact Groovy Web AppsRatpack - Classy and Compact Groovy Web Apps
Ratpack - Classy and Compact Groovy Web Apps
 
Spring boot 3g
Spring boot 3gSpring boot 3g
Spring boot 3g
 
Scripting Oracle Develop 2007
Scripting Oracle Develop 2007Scripting Oracle Develop 2007
Scripting Oracle Develop 2007
 
Getting Productive my Journey with Grakn and Graql
Getting Productive my Journey with Grakn and GraqlGetting Productive my Journey with Grakn and Graql
Getting Productive my Journey with Grakn and Graql
 
Griffon: Swing just got fun again
Griffon: Swing just got fun againGriffon: Swing just got fun again
Griffon: Swing just got fun again
 
Go for Mobile Games
Go for Mobile GamesGo for Mobile Games
Go for Mobile Games
 
Graphlab Create 簡介
Graphlab Create 簡介Graphlab Create 簡介
Graphlab Create 簡介
 
Feelin' Groovy: A Groovy Developer in the Java World
Feelin' Groovy: A Groovy Developer in the Java WorldFeelin' Groovy: A Groovy Developer in the Java World
Feelin' Groovy: A Groovy Developer in the Java World
 
Use groovy & grails in your spring boot projects
Use groovy & grails in your spring boot projectsUse groovy & grails in your spring boot projects
Use groovy & grails in your spring boot projects
 
Griffon: Re-imaging Desktop Java Technology
Griffon: Re-imaging Desktop Java TechnologyGriffon: Re-imaging Desktop Java Technology
Griffon: Re-imaging Desktop Java Technology
 
Django: Beyond Basics
Django: Beyond BasicsDjango: Beyond Basics
Django: Beyond Basics
 
Skyfall b sides-c00-l-ed5-sp-2013
Skyfall b sides-c00-l-ed5-sp-2013Skyfall b sides-c00-l-ed5-sp-2013
Skyfall b sides-c00-l-ed5-sp-2013
 

More from GR8Conf

More from GR8Conf (20)

DevOps Enabling Your Team
DevOps Enabling Your TeamDevOps Enabling Your Team
DevOps Enabling Your Team
 
Creating and testing REST contracts with Accurest Gradle
Creating and testing REST contracts with Accurest Gradle Creating and testing REST contracts with Accurest Gradle
Creating and testing REST contracts with Accurest Gradle
 
Mum, I want to be a Groovy full-stack developer
Mum, I want to be a Groovy full-stack developerMum, I want to be a Groovy full-stack developer
Mum, I want to be a Groovy full-stack developer
 
Metaprogramming with Groovy
Metaprogramming with GroovyMetaprogramming with Groovy
Metaprogramming with Groovy
 
Scraping with Geb
Scraping with GebScraping with Geb
Scraping with Geb
 
How to create a conference android app with Groovy and Android
How to create a conference android app with Groovy and AndroidHow to create a conference android app with Groovy and Android
How to create a conference android app with Groovy and Android
 
Ratpack On the Docks
Ratpack On the DocksRatpack On the Docks
Ratpack On the Docks
 
Groovy Powered Clean Code
Groovy Powered Clean CodeGroovy Powered Clean Code
Groovy Powered Clean Code
 
Cut your Grails application to pieces - build feature plugins
Cut your Grails application to pieces - build feature pluginsCut your Grails application to pieces - build feature plugins
Cut your Grails application to pieces - build feature plugins
 
Performance tuning Grails applications
 Performance tuning Grails applications Performance tuning Grails applications
Performance tuning Grails applications
 
Ratpack and Grails 3
 Ratpack and Grails 3 Ratpack and Grails 3
Ratpack and Grails 3
 
Grails & DevOps: continuous integration and delivery in the cloud
Grails & DevOps: continuous integration and delivery in the cloudGrails & DevOps: continuous integration and delivery in the cloud
Grails & DevOps: continuous integration and delivery in the cloud
 
Functional testing your Grails app with GEB
Functional testing your Grails app with GEBFunctional testing your Grails app with GEB
Functional testing your Grails app with GEB
 
Deploying, Scaling, and Running Grails on AWS and VPC
Deploying, Scaling, and Running Grails on AWS and VPCDeploying, Scaling, and Running Grails on AWS and VPC
Deploying, Scaling, and Running Grails on AWS and VPC
 
Idiomatic spock
Idiomatic spockIdiomatic spock
Idiomatic spock
 
The Groovy Ecosystem Revisited
The Groovy Ecosystem RevisitedThe Groovy Ecosystem Revisited
The Groovy Ecosystem Revisited
 
Groovy 3 and the new Groovy Meta Object Protocol in examples
Groovy 3 and the new Groovy Meta Object Protocol in examplesGroovy 3 and the new Groovy Meta Object Protocol in examples
Groovy 3 and the new Groovy Meta Object Protocol in examples
 
Integration using Apache Camel and Groovy
Integration using Apache Camel and GroovyIntegration using Apache Camel and Groovy
Integration using Apache Camel and Groovy
 
CRaSH the shell for the Java Virtual Machine
CRaSH the shell for the Java Virtual MachineCRaSH the shell for the Java Virtual Machine
CRaSH the shell for the Java Virtual Machine
 
Grooscript gr8conf
Grooscript gr8confGrooscript gr8conf
Grooscript gr8conf
 

Recently uploaded

Architecting Cloud Native Applications
Architecting Cloud Native ApplicationsArchitecting Cloud Native Applications
Architecting Cloud Native Applications
WSO2
 
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers:  A Deep Dive into Serverless Spatial Data and FMECloud Frontiers:  A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Safe Software
 
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Victor Rentea
 

Recently uploaded (20)

Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
 
Corporate and higher education May webinar.pptx
Corporate and higher education May webinar.pptxCorporate and higher education May webinar.pptx
Corporate and higher education May webinar.pptx
 
Architecting Cloud Native Applications
Architecting Cloud Native ApplicationsArchitecting Cloud Native Applications
Architecting Cloud Native Applications
 
WSO2's API Vision: Unifying Control, Empowering Developers
WSO2's API Vision: Unifying Control, Empowering DevelopersWSO2's API Vision: Unifying Control, Empowering Developers
WSO2's API Vision: Unifying Control, Empowering Developers
 
presentation ICT roal in 21st century education
presentation ICT roal in 21st century educationpresentation ICT roal in 21st century education
presentation ICT roal in 21st century education
 
Six Myths about Ontologies: The Basics of Formal Ontology
Six Myths about Ontologies: The Basics of Formal OntologySix Myths about Ontologies: The Basics of Formal Ontology
Six Myths about Ontologies: The Basics of Formal Ontology
 
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers:  A Deep Dive into Serverless Spatial Data and FMECloud Frontiers:  A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
 
Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin WoodPolkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
 
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
 
MINDCTI Revenue Release Quarter One 2024
MINDCTI Revenue Release Quarter One 2024MINDCTI Revenue Release Quarter One 2024
MINDCTI Revenue Release Quarter One 2024
 
CNIC Information System with Pakdata Cf In Pakistan
CNIC Information System with Pakdata Cf In PakistanCNIC Information System with Pakdata Cf In Pakistan
CNIC Information System with Pakdata Cf In Pakistan
 
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost SavingRepurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
 
Strategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a FresherStrategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a Fresher
 
DBX First Quarter 2024 Investor Presentation
DBX First Quarter 2024 Investor PresentationDBX First Quarter 2024 Investor Presentation
DBX First Quarter 2024 Investor Presentation
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
Understanding the FAA Part 107 License ..
Understanding the FAA Part 107 License ..Understanding the FAA Part 107 License ..
Understanding the FAA Part 107 License ..
 
Vector Search -An Introduction in Oracle Database 23ai.pptx
Vector Search -An Introduction in Oracle Database 23ai.pptxVector Search -An Introduction in Oracle Database 23ai.pptx
Vector Search -An Introduction in Oracle Database 23ai.pptx
 
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, AdobeApidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
 
Biography Of Angeliki Cooney | Senior Vice President Life Sciences | Albany, ...
Biography Of Angeliki Cooney | Senior Vice President Life Sciences | Albany, ...Biography Of Angeliki Cooney | Senior Vice President Life Sciences | Albany, ...
Biography Of Angeliki Cooney | Senior Vice President Life Sciences | Albany, ...
 
Mcleodganj Call Girls 🥰 8617370543 Service Offer VIP Hot Model
Mcleodganj Call Girls 🥰 8617370543 Service Offer VIP Hot ModelMcleodganj Call Girls 🥰 8617370543 Service Offer VIP Hot Model
Mcleodganj Call Girls 🥰 8617370543 Service Offer VIP Hot Model
 

The Grails introduction workshop