Scaffold Generator
The Scaffold Generator creates a complete set of files for a resource, including models, endpoints, contracts, pages, and templates. This is the most comprehensive generator for creating full CRUD functionality.
Usage
azu generate scaffold RESOURCE_NAME [OPTIONS]
Description
The scaffold generator creates a complete set of files needed for a full-featured resource with CRUD (Create, Read, Update, Delete) operations. It generates models, endpoints, contracts, pages, templates, and tests all at once, providing a solid foundation for building resource-based features.
Options
RESOURCE_NAME
- Name of the resource to scaffold (required)-d, --description DESCRIPTION
- Description of the resource-a, --attributes ATTRIBUTES
- Comma-separated list of attributes with types-t, --template TEMPLATE
- Template to use (default: basic)-f, --force
- Overwrite existing files-h, --help
- Show help message
Examples
Generate a basic scaffold
azu generate scaffold User
This creates:
src/models/user.cr
- The model classsrc/endpoints/users/
- All endpoint filessrc/contracts/users/
- All contract filessrc/pages/users/
- All page filessrc/db/migrations/TIMESTAMP_create_users.cr
- Migration filespec/
- All test files
Generate a scaffold with attributes
azu generate scaffold Post --attributes "title:string,content:text,author_id:integer,published:boolean"
Generate a scaffold with description
azu generate scaffold Product --description "E-commerce products with inventory management"
Generated Files
Model (src/models/RESOURCE_NAME.cr
)
src/models/RESOURCE_NAME.cr
)# <%= @description || @name.underscore.humanize %> model
class <%= @name.camelcase %> < CQL::Model
table :<%= @name.underscore.pluralize %>
# Generated columns based on attributes
# column :name, String
# column :email, String
# column :age, Int32
# Timestamps
timestamps
# Validations
validates :name, presence: true
# Add more validations as needed
# Associations
# has_many :posts, Post
# belongs_to :user, User
end
Migration (src/db/migrations/TIMESTAMP_create_RESOURCE_NAME.cr
)
src/db/migrations/TIMESTAMP_create_RESOURCE_NAME.cr
)class Create<%= @name.camelcase.pluralize %> < CQL::Migration
def up
create_table :<%= @name.underscore.pluralize %> do |t|
# Generated columns based on attributes
# t.string :name, null: false
# t.string :email, null: false, unique: true
# t.integer :age, null: true
t.timestamps
end
# Add indexes
# add_index :<%= @name.underscore.pluralize %>, :email, unique: true
end
def down
drop_table :<%= @name.underscore.pluralize %>
end
end
Endpoints (src/endpoints/RESOURCE_NAME/
)
src/endpoints/RESOURCE_NAME/
)Index Endpoint (index_endpoint.cr
)
index_endpoint.cr
)class <%= @name.camelcase.pluralize %>::IndexEndpoint < Azu::Endpoint
def call(context : Azu::Context) : Azu::Response
@<%= @name.underscore.pluralize %> = <%= @name.camelcase %>.all
render "endpoints/<%= @name.underscore.pluralize %>/index.json"
end
end
Show Endpoint (show_endpoint.cr
)
show_endpoint.cr
)class <%= @name.camelcase.pluralize %>::ShowEndpoint < Azu::Endpoint
def call(context : Azu::Context) : Azu::Response
@<%= @name.underscore %> = <%= @name.camelcase %>.find(context.params["id"])
unless @<%= @name.underscore %>
return Azu::Response.new(status: 404, body: {error: "Not found"}.to_json)
end
render "endpoints/<%= @name.underscore.pluralize %>/show.json"
end
end
Create Endpoint (create_endpoint.cr
)
create_endpoint.cr
)class <%= @name.camelcase.pluralize %>::CreateEndpoint < Azu::Endpoint
def call(context : Azu::Context) : Azu::Response
contract = <%= @name.camelcase %>Contract.new(context.params.to_h)
unless contract.valid?
return Azu::Response.new(
status: 422,
body: {errors: contract.errors}.to_json
)
end
@<%= @name.underscore %> = <%= @name.camelcase %>.create(contract.valid_data)
render "endpoints/<%= @name.underscore.pluralize %>/create.json", status: 201
end
end
Update Endpoint (update_endpoint.cr
)
update_endpoint.cr
)class <%= @name.camelcase.pluralize %>::UpdateEndpoint < Azu::Endpoint
def call(context : Azu::Context) : Azu::Response
@<%= @name.underscore %> = <%= @name.camelcase %>.find(context.params["id"])
unless @<%= @name.underscore %>
return Azu::Response.new(status: 404, body: {error: "Not found"}.to_json)
end
contract = <%= @name.camelcase %>Contract.new(context.params.to_h)
unless contract.valid?
return Azu::Response.new(
status: 422,
body: {errors: contract.errors}.to_json
)
end
@<%= @name.underscore %>.update(contract.valid_data)
render "endpoints/<%= @name.underscore.pluralize %>/update.json"
end
end
Destroy Endpoint (destroy_endpoint.cr
)
destroy_endpoint.cr
)class <%= @name.camelcase.pluralize %>::DestroyEndpoint < Azu::Endpoint
def call(context : Azu::Context) : Azu::Response
@<%= @name.underscore %> = <%= @name.camelcase %>.find(context.params["id"])
unless @<%= @name.underscore %>
return Azu::Response.new(status: 404, body: {error: "Not found"}.to_json)
end
@<%= @name.underscore %>.delete
Azu::Response.new(status: 204)
end
end
Contracts (src/contracts/RESOURCE_NAME/
)
src/contracts/RESOURCE_NAME/
)Base Contract (contract.cr
)
contract.cr
)class <%= @name.camelcase %>Contract < Azu::Contract
# Generated fields based on attributes
# field :name, String, required: true
# field :email, String, required: true, format: /^[^@]+@[^@]+\.[^@]+$/
# field :age, Int32, min: 0, max: 150
# Custom validations
# def validate_custom_rule
# # Add custom validation logic
# end
end
Pages (src/pages/RESOURCE_NAME/
)
src/pages/RESOURCE_NAME/
)Index Page (index_page.cr
)
index_page.cr
)class <%= @name.camelcase.pluralize %>Page < Azu::Page
def call(context : Azu::Context) : String
@<%= @name.underscore.pluralize %> = <%= @name.camelcase %>.all
@total_count = <%= @name.camelcase %>.count
render "pages/<%= @name.underscore.pluralize %>/index.jinja"
end
end
Show Page (show_page.cr
)
show_page.cr
)class <%= @name.camelcase %>ShowPage < Azu::Page
def call(context : Azu::Context) : String
@<%= @name.underscore %> = <%= @name.camelcase %>.find(context.params["id"])
unless @<%= @name.underscore %>
context.response.status_code = 404
return render "pages/404.jinja"
end
render "pages/<%= @name.underscore.pluralize %>/show.jinja"
end
end
New Page (new_page.cr
)
new_page.cr
)class <%= @name.camelcase %>NewPage < Azu::Page
def call(context : Azu::Context) : String
@<%= @name.underscore %> = <%= @name.camelcase %>.new
@errors = context.flash["errors"]?
render "pages/<%= @name.underscore.pluralize %>/new.jinja"
end
end
Edit Page (edit_page.cr
)
edit_page.cr
)class <%= @name.camelcase %>EditPage < Azu::Page
def call(context : Azu::Context) : String
@<%= @name.underscore %> = <%= @name.camelcase %>.find(context.params["id"])
unless @<%= @name.underscore %>
context.response.status_code = 404
return render "pages/404.jinja"
end
@errors = context.flash["errors"]?
render "pages/<%= @name.underscore.pluralize %>/edit.jinja"
end
end
Attribute Types
Supported Attribute Types
# String attributes
azu generate scaffold User --attributes "name:string,email:string,username:string"
# Text attributes
azu generate scaffold Post --attributes "title:string,content:text,excerpt:text"
# Numeric attributes
azu generate scaffold Product --attributes "name:string,price:decimal,quantity:integer,weight:float"
# Boolean attributes
azu generate scaffold Post --attributes "title:string,content:text,published:boolean,featured:boolean"
# Date/Time attributes
azu generate scaffold Event --attributes "name:string,start_date:date,end_date:date,created_at:datetime"
# Reference attributes (foreign keys)
azu generate scaffold Post --attributes "title:string,content:text,author_id:integer,category_id:integer"
Attribute Options
# With constraints
azu generate scaffold User --attributes "name:string:required,email:string:required:unique,age:integer:min:18"
# With defaults
azu generate scaffold Post --attributes "title:string:required,content:text:required,published:boolean:default:false"
Scaffold Patterns
Basic CRUD Scaffold
azu generate scaffold User --attributes "name:string,email:string,age:integer"
This creates a complete CRUD interface for users with:
User model with validations
Database migration
RESTful endpoints (index, show, create, update, destroy)
Validation contracts
Web pages (index, show, new, edit)
HTML templates
Test files
Blog Post Scaffold
azu generate scaffold Post --attributes "title:string,content:text,author_id:integer,published:boolean,slug:string"
This creates a blog post system with:
Post model with associations
Migration with indexes
Full CRUD endpoints
Form validation
Web interface
SEO-friendly URLs
E-commerce Product Scaffold
azu generate scaffold Product --attributes "name:string,description:text,price:decimal,sku:string,stock:integer,category_id:integer"
This creates a product management system with:
Product model with inventory tracking
Price and stock management
Category associations
SKU validation
Complete admin interface
Using Scaffolds
Route Registration
Register scaffold routes in your application:
class Application < Azu::Application
# RESTful routes for the scaffolded resource
resources :users do
get "/", Users::IndexEndpoint
get "/new", Users::NewPage
post "/", Users::CreateEndpoint
get "/:id", Users::ShowEndpoint
get "/:id/edit", Users::EditPage
put "/:id", Users::UpdateEndpoint
delete "/:id", Users::DestroyEndpoint
end
end
Customizing Generated Code
After generating a scaffold, you can customize the generated files:
# Add custom validations to the model
class User < CQL::Model
table :users
column :name, String
column :email, String
validates :name, presence: true, length: {minimum: 2}
validates :email, presence: true, format: /^[^@]+@[^@]+\.[^@]+$/
# Add custom methods
def full_name
"#{first_name} #{last_name}"
end
end
Adding Associations
# Add associations to the model
class Post < CQL::Model
table :posts
column :title, String
column :content, Text
column :author_id, Int64
belongs_to :author, User
has_many :comments, Comment
end
Best Practices
1. Plan Your Attributes
Think carefully about your resource attributes before scaffolding:
# Good: Well-planned attributes
azu generate scaffold User --attributes "first_name:string,last_name:string,email:string:unique,password_digest:string,role:string:default:user"
# Good: Include necessary associations
azu generate scaffold Post --attributes "title:string,content:text,author_id:integer,published:boolean,slug:string:unique"
2. Use Appropriate Data Types
# Good: Appropriate types for each attribute
azu generate scaffold Product --attributes "name:string,description:text,price:decimal,sku:string:unique,stock:integer,active:boolean"
# Avoid: Using string for everything
azu generate scaffold Product --attributes "name:string,description:string,price:string,sku:string,stock:string"
3. Include Validations
# Good: Include validation constraints
azu generate scaffold User --attributes "name:string:required,email:string:required:unique,age:integer:min:18:max:120"
4. Consider Performance
# Good: Include indexes for frequently queried fields
azu generate scaffold Post --attributes "title:string,content:text,author_id:integer:index,published:boolean:index,created_at:datetime:index"
Testing Scaffolds
Model Testing
describe User do
describe "validations" do
it "is valid with correct attributes" do
user = User.new(name: "John Doe", email: "john@example.com")
user.valid?.should be_true
end
it "requires name" do
user = User.new(email: "john@example.com")
user.valid?.should be_false
user.errors[:name].should contain("can't be blank")
end
end
end
Endpoint Testing
describe Users::IndexEndpoint do
it "returns all users" do
user = User.create(name: "John Doe", email: "john@example.com")
get "/users"
response.status_code.should eq(200)
response.body.should contain("John Doe")
end
end
Page Testing
describe UsersPage do
it "displays users list" do
user = User.create(name: "John Doe", email: "john@example.com")
get "/users"
response.status_code.should eq(200)
response.body.should contain("John Doe")
response.body.should contain("john@example.com")
end
end
Common Scaffold Patterns
1. User Management
azu generate scaffold User --attributes "first_name:string,last_name:string,email:string:unique,password_digest:string,role:string:default:user,active:boolean:default:true"
2. Content Management
azu generate scaffold Post --attributes "title:string,content:text,excerpt:text,author_id:integer,slug:string:unique,published:boolean:default:false,published_at:datetime"
3. E-commerce
azu generate scaffold Product --attributes "name:string,description:text,price:decimal,sku:string:unique,stock:integer,weight:float,category_id:integer,active:boolean:default:true"
4. Event Management
azu generate scaffold Event --attributes "title:string,description:text,start_date:date,end_date:date,location:string,capacity:integer,organizer_id:integer,status:string:default:draft"
Related Commands
azu generate model
- Generate data modelsazu generate endpoint
- Generate API endpointsazu generate contract
- Generate validation contractsazu generate page
- Generate web pagesazu generate migration
- Generate database migrations
Templates
The scaffold generator supports different templates:
basic
- Basic CRUD scaffold templateapi
- API-only scaffold templateweb
- Web-focused scaffold templateadmin
- Admin interface scaffold template
To use a specific template:
azu generate scaffold User --template api
Last updated