Vapor Generators
VaporGenerators is a package that extends Vapor to add a command line tool that generates classes and files automagically for you. It is very much inspired from Rails' command line tools.
Compatibility
This Generators have been tested on macOS. Linux users beware: this might not work for you!
Installation
You can install the generators using 3 simple steps:
- Add this Package as a dependency to your project
- Configure your
Droplet
with theGenerate
command. - Build your project
Step 1. Add this Package to your project
// In your project's Package.swift file
Package(
...
dependencies: [
.Package(url: "https://github.com/gtranchedone/VaporGenerators.git", majorVersion: 0),
...
]
...
)
Step 2. Configure your Droplet
// In your project's main.swift file
let drop = Droplet()
...
drop.commands.append(Generate(console: drop.console))
drop.run()
Step 3. Build your project
# In a Terminal window
cd /path/to/your/project
swift build
Usage
After you've installed the Generate
command, you can use the generator from the command line, like so:
# In a Terminal window
cd /path/to/your/project
vapor run generate [generator-type] [arguments]
vapor build # or
vapor xcode # if you use Xcode or AppCode
See the list of generator types below for details of what generators are available and what arguments they accept.
Available Generators
model
Generates a model class and an associated test class.
For example, running the command vapor run generate model foo
would produce the classes highlighted by this structure:
MyProject
├── ...
├── Sources
| └── App
| ├── ...
| └── Models
| ├── ...
| └── Foo.swift
└── Tests
└── AppTests
└── Models
├── ...
└── FooTests.swift
Parameters
name (required)
The name of the model. The generator always capitalizes this parameter before using it.
properties (optional)
A space separated list of strings in the format propertyName:propertyType
.
E.g. vapor run generate model user firstName:string lastName:string
would generate the following:
import Vapor
import Fluent
import Foundation
final class User: Model {
fileprivate static let tableName = "users"
var id: Node?
var firstName: String
var lastName: String
init(node: Node, in context: Context) throws {
id = try node.extract("id")
firstName = try node.extract("first_name")
lastName = try node.extract("last_name")
}
func makeNode(context: Context) throws -> Node {
return try Node(node: [
"id": id,
"first_name": firstName,
"last_name": lastName,
])
}
}
extension User: Preparation {
static func prepare(_ database: Database) throws {
try database.create(User.tableName) {
$0.id()
$0.string("first_name")
$0.string("last_name")
}
}
static func revert(_ database: Database) throws {
try database.delete(User.tableName)
}
}
view
Generate Leaf views.
Parameters
names (required)
A list of names for the views to generate.
--useFirstArgumentAsDirectory (optional)
If this flag is passed, the first name in the list is used as a directory name in which all other views are generated.
controller
Generate a controller class. By default the command creates an empty controller. If you pass the --resource
flag, it creates a controller that conforms to the Vapor.ResourceRepresentable
protocol instead.
Parameters
name (required)
The name of the controller. This can be either a singular resource name such as Post
or a fully formed controller name such as PostsController
. In both cases, the name PostsController
is the final name the generator would use.
Example outputs
- "Post" => "PostsController"
- "PostsController" => "SettingsController"
- "Keyboard" => "KeyboardsController"
- "KeyboardController" => "KeyboardController"
actions (optional)
A space separated list of strings. By default this represent methods that the generated controller will implement. When the --resource
flag is passed to the generator, the actions are interpreted as names of the Vapor.Resource
actions to be implemented.
Examples
The command vapor run generate controller MyController foo bar
would produce
import Vapor
import HTTP
final class MyController {
let droplet: Droplet
init(droplet: Droplet) {
self.droplet = droplet
}
func foo(request: request) throws -> ResourceRepresentable {
return try droplet.view.make("my/foo")
}
func bar(request: request) throws -> ResourceRepresentable {
return try droplet.view.make("my/bar")
}
}
route
Generates a route with the specified parameters.
Parameters
path (required)
The path to created relative to the root.
method (optional)
The HTTP method to use when creating the path. Defaults to get
.
handler (optional)
A string representing code to execute as part of the route's handling closure. E.g. try droplet.view.make("index")
.
NOTE: Although available, you shouldn't really use this method. Simply generate the route and then use your favorite text editor or IDE to change the route's handling implementation.
--resource (optional)
When specified, the generator creates resource paths using Vapor's resource
method and ignores the method
and handler
parameters.
Examples
vapor run generate route foo
// In your routes file
droplet.get("foo") { request in
return JSON([:])
}
vapor run generate route bar --resource
// In your routes file
droplet.resource("bars", BarsController())
...
extension Droplet {
func pathForBars() -> String {
return "bars"
}
func path(for model: _RESOURCE_) throws -> String {
guard let id = model.id?.int else { throw Abort.badRequest }
return pathForBars() + "/\(id)"
}
func pathForCreatingBars() throws -> String {
return try pathForBars() + "/new"
}
func path(forEditing model: _RESOURCE_) throws -> String {
return try path(for: model) + "/edit"
}
}
resource
This produces a Model, Views, a Controller and Routes using the respective generators and passing the arguments along. Additionally, it creates style and script files for the specified resource.
For example, running vapor run generate resource user index show firstName:string lastName:string
would create the following files:
MyProject
├── ...
├── Public
| ├── scripts
| | ├── ...
| | └── users.js
| └── styles
| ├── ...
| └── users.css
├── Resources
| └── Views
| ├── ...
| └── users
| ├── index.leaf
| └── show.leaf
├── Sources
| └── App
| ├── ...
| ├── Models
| | ├── ...
| | └── User.swift
| └── Controllers
| ├── ...
| └── UsersController.swift
└── Tests
└── AppTests
├── ...
├── Models
| ├── ...
| └── UserTests.swift
└── Controllers
├── ...
└── MyControllerTests.swift
See the other generator's details to see how each file's content is generated.
Parameters
See the parameters accepted by the other generators.
tests
Generates a test class for the passed in class name. It also creates a LinuxMain.swift
file if one is not found and updates it for each generated test case.
Parameters
className (required)
The name of the class for which you want to generate tests or a the name of the test class itself. Valid inputs are:
MyController
=> will produceMyControllerTests
MyControllerTests
=> will produceMyControllerTests
TestMyController
=> will produceTestMyController
directory (optional)
The path where the test class is to be generated. Defaults to Tests/AppTests/
. An example input would be Tests/AppTests/Controllers/
.
Customization
You can customize the generate
command to use your own generators like so:
// In your project's main.swift file
let generateCommand = Generate(console: droplet.console)
generateCommand["model"] = MyModelGenerator.self
generateCommand["my-custom-generator"] = MyCustomGenerator.self
drop.commands.append(generateCommand)
To use your custom generators, you would run the same command as before:
# In a Terminal window
cd /path/to/your/project
vapor run generate model [arguments]
vapor run generate my-custom-generator [arguments]
TODO
- Test on Linux
- Add Unit Tests
- Improve Pluralization code (and extract it in a new Package)