skinny-framework/skinny-framework — pull request #208 by seratch (2014-12-06 17:57:57+00:00)
Title: Add reverse-scaffold-all/reverse-model-all command

Powerful reverse engineering commands.

## reverse-model-all

### DDL

```sql
create table company (
id bigserial not null primary key,
name varchar(100) not null,
url varchar(512),
created_at timestamp not null default current_timestamp
);

create table member (
id bigserial not null primary key,
name varchar(512) not null,
nickname varchar(32),
company_id bigint references company(id),
joined_at timestamp not null,
leave_at timestamp
);
```

### Output

```
*** Skinny Reverse Engineering Task ***

Table : company
ID : id:Long

Columns:
- name:String:varchar(100)
- url:Option[String]:varchar(512)
- createdAt:DateTime
- members:Seq[Member]

*** Skinny Generator Task ***

"tmp/ReverseModelAllGeneratorSpec/src/main/scala/model/Company.scala" created.
"tmp/ReverseModelAllGeneratorSpec/src/test/scala/model/CompanySpec.scala" created.

*** Skinny Reverse Engineering Task ***

Table : member
ID : id:Long

Columns:
- name:String:varchar(512)
- nickname:Option[String]:varchar(32)
- companyId:Option[Long]
- joinedAt:DateTime
- leaveAt:Option[DateTime]
- company:Option[Company]

*** Skinny Generator Task ***

"tmp/ReverseModelAllGeneratorSpec/src/main/scala/model/Member.scala" created.
"tmp/ReverseModelAllGeneratorSpec/src/test/scala/model/MemberSpec.scala" created.
```

### Generated Files

```scala
package model

import skinny.orm._, feature._
import scalikejdbc._
import org.joda.time._

// If your model has +23 fields, switch this to normal class and mixin scalikejdbc.EntityEquality.
case class Company(
id: Long,
name: String,
url: Option[String] = None,
createdAt: DateTime,
members: Seq[Member] = Nil
)

object Company extends SkinnyCRUDMapper[Company] {
override lazy val tableName = "company"
override lazy val defaultAlias = createAlias("c")

lazy val membersRef = hasMany[Member](
many = Member -> Member.defaultAlias,
on = (c, m) => sqls.eq(c.id, m.companyId),
merge = (c, ms) => c.copy(members = ms)
)

override def extract(rs: WrappedResultSet, rn: ResultName[Company]): Company = {
autoConstruct(rs, rn, "members")
}
}
```

```scala
package model

import skinny.orm._, feature._
import scalikejdbc._
import org.joda.time._

// If your model has +23 fields, switch this to normal class and mixin scalikejdbc.EntityEquality.
case class Member(
id: Long,
name: String,
nickname: Option[String] = None,
companyId: Option[Long] = None,
joinedAt: DateTime,
leaveAt: Option[DateTime] = None,
company: Option[Company] = None
)

object Member extends SkinnyCRUDMapper[Member] {
override lazy val tableName = "member"
override lazy val defaultAlias = createAlias("m")

lazy val companyRef = belongsTo[Company](Company, (m, c) => m.copy(company = c))

override def extract(rs: WrappedResultSet, rn: ResultName[Member]): Member = {
autoConstruct(rs, rn, "company")
}
}
```

## reverse-scaffold-all

### DDL

```sql
create table user_group (
id bigserial not null primary key,
name varchar(100) not null,
url varchar(512)
);
create table organization (
id bigserial not null primary key,
name varchar(100) not null,
url varchar(512) not null
);
create table developer (
id bigserial not null primary key,
name varchar(512) not null,
nickname varchar(32),
user_group_id bigint references user_group(id)
);
create table organization_developer (
organization_id bigint not null references organization(id),
developer_id bigint not null references developer(id)
);
```

### Output

```
*** Skinny Reverse Engineering Task ***

Table : developer
ID : id:Long
Resources : developers
Resource : developer

Columns:
- name:String:varchar(512)
- nickname:Option[String]:varchar(32)
- userGroupId:Option[Long]
- userGroup:Option[UserGroup]
- organizationDevelopers:Seq[OrganizationDeveloper]

*** Skinny Generator Task ***

"tmp/ReverseScaffoldAllGeneratorSpec/src/main/scala/controller/ApplicationController.scala" skipped.
"tmp/ReverseScaffoldAllGeneratorSpec/src/main/scala/controller/DevelopersController.scala" created.
"tmp/ReverseScaffoldAllGeneratorSpec/src/main/scala/controller/Controllers.scala" modified.
"tmp/ReverseScaffoldAllGeneratorSpec/src/test/scala/controller/DevelopersControllerSpec.scala" created.
"tmp/ReverseScaffoldAllGeneratorSpec/src/test/scala/integrationtest/DevelopersController_IntegrationTestSpec.scala" created.
"tmp/ReverseScaffoldAllGeneratorSpec/src/test/resources/factories.conf" modified.
"tmp/ReverseScaffoldAllGeneratorSpec/src/main/scala/model/Developer.scala" created.
"tmp/ReverseScaffoldAllGeneratorSpec/src/test/scala/model/DeveloperSpec.scala" created.
"tmp/ReverseScaffoldAllGeneratorSpec/src/main/webapp/WEB-INF/views/developers/_form.html.ssp" created.
"tmp/ReverseScaffoldAllGeneratorSpec/src/main/webapp/WEB-INF/views/developers/new.html.ssp" created.
"tmp/ReverseScaffoldAllGeneratorSpec/src/main/webapp/WEB-INF/views/developers/edit.html.ssp" created.
"tmp/ReverseScaffoldAllGeneratorSpec/src/main/webapp/WEB-INF/views/developers/index.html.ssp" created.
"tmp/ReverseScaffoldAllGeneratorSpec/src/main/webapp/WEB-INF/views/developers/show.html.ssp" created.
"tmp/ReverseScaffoldAllGeneratorSpec/src/main/resources/messages.conf" modified.
"tmp/ReverseScaffoldAllGeneratorSpec/src/main/resources/db/migration/V20141207024947__Create_developers_table.sql" created.

*** Skinny Reverse Engineering Task ***

Table : organization
ID : id:Long
Resources : organizations
Resource : organization

Columns:
- name:String:varchar(100)
- url:String:varchar(512)
- organizationDevelopers:Seq[OrganizationDeveloper]

*** Skinny Generator Task ***

"tmp/ReverseScaffoldAllGeneratorSpec/src/main/scala/controller/ApplicationController.scala" skipped.
"tmp/ReverseScaffoldAllGeneratorSpec/src/main/scala/controller/OrganizationsController.scala" created.
"tmp/ReverseScaffoldAllGeneratorSpec/src/main/scala/controller/Controllers.scala" modified.
"tmp/ReverseScaffoldAllGeneratorSpec/src/test/scala/controller/OrganizationsControllerSpec.scala" created.
"tmp/ReverseScaffoldAllGeneratorSpec/src/test/scala/integrationtest/OrganizationsController_IntegrationTestSpec.scala" created.
"tmp/ReverseScaffoldAllGeneratorSpec/src/test/resources/factories.conf" modified.
"tmp/ReverseScaffoldAllGeneratorSpec/src/main/scala/model/Organization.scala" created.
"tmp/ReverseScaffoldAllGeneratorSpec/src/test/scala/model/OrganizationSpec.scala" created.
"tmp/ReverseScaffoldAllGeneratorSpec/src/main/webapp/WEB-INF/views/organizations/_form.html.ssp" created.
"tmp/ReverseScaffoldAllGeneratorSpec/src/main/webapp/WEB-INF/views/organizations/new.html.ssp" created.
"tmp/ReverseScaffoldAllGeneratorSpec/src/main/webapp/WEB-INF/views/organizations/edit.html.ssp" created.
"tmp/ReverseScaffoldAllGeneratorSpec/src/main/webapp/WEB-INF/views/organizations/index.html.ssp" created.
"tmp/ReverseScaffoldAllGeneratorSpec/src/main/webapp/WEB-INF/views/organizations/show.html.ssp" created.
"tmp/ReverseScaffoldAllGeneratorSpec/src/main/resources/messages.conf" modified.
"tmp/ReverseScaffoldAllGeneratorSpec/src/main/resources/db/migration/V20141207024947__Create_organizations_table.sql" created.

Since this table (organization_developer) has no primary key, generator created only NoIdCRUDMapper file and skipped creating controller and view files.

*** Skinny Reverse Engineering Task ***

Table : organization_developer

Columns:
- organizationId:Long
- developerId:Long
- developer:Option[Developer]
- organization:Option[Organization]

*** Skinny Generator Task ***

"tmp/ReverseScaffoldAllGeneratorSpec/src/main/scala/model/OrganizationDeveloper.scala" created.
"tmp/ReverseScaffoldAllGeneratorSpec/src/test/scala/model/OrganizationDeveloperSpec.scala" created.
"tmp/ReverseScaffoldAllGeneratorSpec/src/main/resources/db/migration/V20141207024947__Create_organizationDevelopers_table.sql" created.

*** Skinny Reverse Engineering Task ***

Table : user_group
ID : id:Long
Resources : userGroups
Resource : userGroup

Columns:
- name:String:varchar(100)
- url:Option[String]:varchar(512)
- developers:Seq[Developer]

*** Skinny Generator Task ***

"tmp/ReverseScaffoldAllGeneratorSpec/src/main/scala/controller/ApplicationController.scala" skipped.
"tmp/ReverseScaffoldAllGeneratorSpec/src/main/scala/controller/UserGroupsController.scala" created.
"tmp/ReverseScaffoldAllGeneratorSpec/src/main/scala/controller/Controllers.scala" modified.
"tmp/ReverseScaffoldAllGeneratorSpec/src/test/scala/controller/UserGroupsControllerSpec.scala" created.
"tmp/ReverseScaffoldAllGeneratorSpec/src/test/scala/integrationtest/UserGroupsController_IntegrationTestSpec.scala" created.
"tmp/ReverseScaffoldAllGeneratorSpec/src/test/resources/factories.conf" modified.
"tmp/ReverseScaffoldAllGeneratorSpec/src/main/scala/model/UserGroup.scala" created.
"tmp/ReverseScaffoldAllGeneratorSpec/src/test/scala/model/UserGroupSpec.scala" created.
"tmp/ReverseScaffoldAllGeneratorSpec/src/main/webapp/WEB-INF/views/userGroups/_form.html.ssp" created.
"tmp/ReverseScaffoldAllGeneratorSpec/src/main/webapp/WEB-INF/views/userGroups/new.html.ssp" created.
"tmp/ReverseScaffoldAllGeneratorSpec/src/main/webapp/WEB-INF/views/userGroups/edit.html.ssp" created.
"tmp/ReverseScaffoldAllGeneratorSpec/src/main/webapp/WEB-INF/views/userGroups/index.html.ssp" created.
"tmp/ReverseScaffoldAllGeneratorSpec/src/main/webapp/WEB-INF/views/userGroups/show.html.ssp" created.
"tmp/ReverseScaffoldAllGeneratorSpec/src/main/resources/messages.conf" modified.
"tmp/ReverseScaffoldAllGeneratorSpec/src/main/resources/db/migration/V20141207024947__Create_userGroups_table.sql" created.
```

### Generated Files

```scala
package model

import skinny.orm._, feature._
import scalikejdbc._
import org.joda.time._

// If your model has +23 fields, switch this to normal class and mixin scalikejdbc.EntityEquality.
case class Developer(
id: Long,
name: String,
nickname: Option[String] = None,
userGroupId: Option[Long] = None,
userGroup: Option[UserGroup] = None,
organizations: Seq[Organization] = Nil
)

object Developer extends SkinnyCRUDMapper[Developer] {
override lazy val tableName = "developer"
override lazy val defaultAlias = createAlias("d")

lazy val userGroupRef = belongsTo[UserGroup](UserGroup, (d, ug) => d.copy(userGroup = ug))

lazy val organizationsRef = hasManyThrough[Organization](
through = OrganizationDeveloper,
many = Organization,
merge = (d, os) => d.copy(organizations = os)
)

override def extract(rs: WrappedResultSet, rn: ResultName[Developer]): Developer = {
autoConstruct(rs, rn, "userGroup", "organizations")
}
}
```

```scala
package controller

import skinny._
import skinny.validator._
import _root_.controller._
import model.Developer

class DevelopersController extends SkinnyResource with ApplicationController {
protectFromForgery()

override def model = Developer
override def resourcesName = "developers"
override def resourceName = "developer"

override def resourcesBasePath = s"/${toSnakeCase(resourcesName)}"
override def useSnakeCasedParamKeys = true

override def viewsDirectoryPath = s"/${resourcesName}"

override def createParams = Params(params)
override def createForm = validation(createParams,
paramKey("name") is required & maxLength(512),
paramKey("nickname") is maxLength(32),
paramKey("user_group_id") is numeric & longValue
)
override def createFormStrongParameters = Seq(
"name" -> ParamType.String,
"nickname" -> ParamType.String,
"user_group_id" -> ParamType.Long
)

override def updateParams = Params(params)
override def updateForm = validation(updateParams,
paramKey("name") is required & maxLength(512),
paramKey("nickname") is maxLength(32),
paramKey("user_group_id") is numeric & longValue
)
override def updateFormStrongParameters = Seq(
"name" -> ParamType.String,
"nickname" -> ParamType.String,
"user_group_id" -> ParamType.Long
)

}
```