├── .github └── workflows │ └── ci.yml ├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── shard.yml ├── spec ├── application_spec.cr ├── cli_spec.cr └── spec_helper.cr └── src ├── fez.cr ├── fez ├── application.cr ├── cli.cr ├── default_options.cr ├── errors.cr └── version.cr └── templates ├── api ├── .env_tmpl ├── .gitignore ├── .travis.yml ├── Capfile ├── Gemfile ├── Guardfile_tmpl ├── Makefile ├── README.md_tmpl ├── app.cr ├── config.cr_tmpl ├── config │ ├── deploy.rb_tmpl │ └── deploy │ │ └── production.rb ├── shard.yml_tmpl ├── spec │ ├── kemal_api_spec.cr │ └── spec_helper.cr └── src │ ├── macros │ └── helper.cr │ └── {{name}}.cr ├── ecr ├── .env_tmpl ├── .gitignore ├── .travis.yml ├── Capfile ├── Gemfile ├── Guardfile_tmpl ├── Makefile ├── README.md_tmpl ├── app.cr ├── config.cr_tmpl ├── config │ ├── deploy.rb_tmpl │ └── deploy │ │ └── production.rb ├── es2js.rb ├── public │ ├── favicon.ico │ └── robots.txt ├── shard.yml_tmpl ├── spec │ ├── kemal_ecr_spec.cr │ └── spec_helper.cr └── src │ ├── assets │ ├── scripts │ │ ├── manifest.yml │ │ └── site.es6 │ └── styles │ │ └── site.scss │ ├── macros │ └── helper.cr │ ├── views │ ├── layouts │ │ └── layout.ecr_tmpl │ └── site │ │ └── index.ecr │ └── {{name}}.cr └── slang ├── .env_tmpl ├── .gitignore ├── .travis.yml ├── Capfile ├── Gemfile ├── Guardfile_tmpl ├── Makefile ├── README.md_tmpl ├── app.cr ├── config.cr_tmpl ├── config ├── deploy.rb_tmpl └── deploy │ └── production.rb ├── es2js.rb ├── public ├── favicon.ico └── robots.txt ├── shard.yml_tmpl ├── spec ├── kemal_slang_spec.cr └── spec_helper.cr └── src ├── assets ├── scripts │ ├── manifest.yml │ └── site.es6 └── styles │ └── site.scss ├── macros └── helper.cr ├── views ├── layouts │ └── layout.slang_tmpl └── site │ └── index.slang └── {{name}}.cr /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Fez CI 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: "*" 8 | 9 | jobs: 10 | check_format: 11 | strategy: 12 | fail-fast: false 13 | matrix: 14 | crystal_version: 15 | - 0.36.1 16 | - 1.0.0 17 | experimental: 18 | - false 19 | runs-on: ubuntu-latest 20 | continue-on-error: ${{ matrix.experimental }} 21 | container: crystallang/crystal:${{ matrix.crystal_version }}-alpine 22 | steps: 23 | - uses: actions/checkout@v1 24 | - name: Install shards 25 | run: shards install 26 | - name: Format 27 | run: crystal tool format --check 28 | - name: Lint 29 | run: ./bin/ameba 30 | specs: 31 | strategy: 32 | fail-fast: false 33 | matrix: 34 | crystal_version: 35 | - 0.36.1 36 | - 1.0.0 37 | experimental: 38 | - false 39 | runs-on: ubuntu-latest 40 | continue-on-error: ${{ matrix.experimental }} 41 | container: crystallang/crystal:${{ matrix.crystal_version }}-alpine 42 | steps: 43 | - uses: actions/checkout@v2 44 | - name: Install dependencies 45 | run: shards install 46 | - name: Run tests 47 | run: crystal spec 48 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /doc/ 3 | /libs/ 4 | /lib/ 5 | /.crystal/ 6 | /.shards/ 7 | /bin 8 | 9 | # So I can put some projects in here and ignore them 10 | /sandbox 11 | 12 | # Libraries don't need dependency lock 13 | # Dependencies will be locked in application that uses them 14 | /shard.lock 15 | 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016-2021 Jeremy Woertink 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | OUT_DIR=bin 2 | 3 | all: build 4 | 5 | build: clean 6 | @echo "Building Fez in $(shell pwd)" 7 | @shards install 8 | @mkdir -p $(OUT_DIR) 9 | @crystal build --release -o $(OUT_DIR)/fez src/fez.cr 10 | 11 | run: 12 | $(OUT_DIR)/fez 13 | 14 | clean: 15 | rm -rf $(OUT_DIR) .crystal .shards libs lib 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # fez [![Build Status](https://travis-ci.com/jwoertink/fez.svg?branch=master)](https://travis-ci.com/jwoertink/fez) 2 | 3 | Fez is an application generator for projects with [Kemal](http://kemalcr.com/). 4 | 5 | Setting up a Kemal app is super simple, but if the app you're building is a little more than "super simple", there's a bit of things you'll end up doing each time you start a project. Why not have a mini generator that will do it for you? 6 | 7 | This generator will generate a somewhat opinionated version of: 8 | 9 | * Simple app with assets and views (customizable between ECR and Slang) setup 10 | * Simple API with api versioning 11 | * Simple app backed by a database (not implemented) 12 | 13 | ## Installation 14 | 15 | Since this is an application generator, there's no need to include it as a dependency, or shard. Just install it locally, and build the executable. 16 | 17 | ```text 18 | $ git clone git@github.com:jwoertink/fez.git 19 | $ cd fez/ 20 | $ make 21 | ``` 22 | You should now have a `bin/fez` file to run. You can symlink this to a more global location like `/usr/local/bin` to make it easier to use. 23 | 24 | Optionally, you can use [homebrew](http://brew.sh) to install. 25 | 26 | ```text 27 | brew tap jwoertink/fez 28 | brew install fez 29 | ``` 30 | 31 | **NOTE** If you have issues running from homebrew, install from github, and report the error to [homebrew-fez](https://github.com/jwoertink/homebrew-fez) 32 | 33 | ## Usage 34 | 35 | Using fez is pretty easy. To see a help menu just run `fez -h`. You can see the version of fez you're running with `fez -v` 36 | 37 | TL;DR `fez yourappname` 38 | 39 | ### Naming your app 40 | 41 | To set the name of your application, you run fez with the name of the project. This is *required* in order to run. 42 | 43 | This example will generate a `./my_cool_app/` folder. 44 | ```text 45 | $ fez my_cool_app 46 | ``` 47 | 48 | ### Specifying the directory name 49 | 50 | Fez gives you the option to create a different name for the directory your app is in than the actual app name. You can do this with the `--directory` or `-d` flag. This flag is optional. 51 | 52 | This example will generate an app called `my_cool_app` in a `myapp.cr` folder. 53 | 54 | ```text 55 | $ fez my_cool_app -d ~/Projects/myapp.cr 56 | 57 | or 58 | 59 | $ fez my_cool_app --directory=~/Projects/myapp.cr 60 | ``` 61 | 62 | If you don't specify a directory, fez will assume you want to use the current directory you're in. 63 | 64 | ### Templates 65 | 66 | Fez comes out of the box with several different templates: 67 | * Kemal with slang 68 | * Kemal with ECR 69 | * Kemal as an API 70 | * more coming later... 71 | 72 | By default, fez uses Kemal with [Slang](https://github.com/jeromegn/slang) as the default template. If you would like to switch back to using Crystal's built in ECR, or build an api, you will use the `--template` or `-t` flag. This flag is optional. 73 | 74 | ```text 75 | $ fez my_cool_app -t ecr 76 | 77 | or 78 | 79 | $ fez my_cool_app --template=ecr 80 | ``` 81 | 82 | ### Generating an API only application 83 | 84 | If you're building an API, and don't need HTML, CSS and JavaScript, you can use Fez to generate an API only app. 85 | 86 | ```text 87 | $ fez my_api -t api 88 | ``` 89 | 90 | By default a JSON API will be generated with route versioning. You can read up on [kave](https://github.com/jwoertink/kave) for more info about api customization. 91 | 92 | 93 | ### Working with a fez generated app 94 | 95 | Now that you've generated your shiny new Kemal app, it's time to start doing development on it. The generated app will contain a `README.md` file that has all the instructions on how to start development. 96 | 97 | Your app will have 2 primary dependencies to run. 98 | * [Ruby](https://www.ruby-lang.org/) 99 | * [Crystal](https://crystal-lang.org/) 100 | 101 | If you have those two installed, then your next step is to `cd` in to your new app's directory and run `make install`. 102 | 103 | ```text 104 | $ cd ~/Projects 105 | Projects $ fez supertrain 106 | Projects $ cd supertrain 107 | Projects/supertrain $ make install 108 | ``` 109 | 110 | Running `make install` will install the shard dependencies for your app (like Kemal, duh!), and then install the ruby gem dependencies. You may be asking yourself _why have ruby gem dependencies?_... Ruby has tools that work great like `guard` and `sass`. Plus, chances are, you came to Crystal from Ruby anyway, and if you're on a Mac, Ruby is installed by default. 111 | 112 | Ok, now that your app dependencies are installed, you have 2 options to boot this baby. 113 | 114 | 1. `make run` - This compiles your assets in to their natural form, and then boots kemal. 115 | 2. `guard` - This will boot your kemal and then watch for any changes to the files. 116 | 117 | Both of these options will boot a server on `localhost:3001`. The difference is that using guard allows you to do live-reloading. 118 | 119 | If you need to use a console (REPL) for development, you can use the `make console` command. You will have access to whatever has been required in your app's `config.cr` 120 | 121 | ```text 122 | Projects/supertrain $ make console 123 | => ok 124 | icr(0.19.1) > Kemal 125 | => Kemal 126 | icr(0.19.1) > 127 | ``` 128 | 129 | Now that you have a handle on developing your app, you will want to deploy it! There's a lot of different methods to deploying an app in to production, and fez takes the [Capistrano](http://capistranorb.com/) appraoch. 130 | 131 | 1. Edit your `config/deploy.rb` file with the necessary changes. 132 | 2. Edit your `config/deploy/production.rb` file with more necessary changes 133 | 3. Run `cap production deploy`. 134 | 4. Pray it all works! 135 | 136 | Alternitively, if you're looking to deploy to [Heroku](https://www.heroku.com/), you can use [Crystal Heroku Build Pack](https://github.com/crystal-lang/heroku-buildpack-crystal/) to deploy. 137 | 138 | ## Development 139 | 140 | If you'd like to help contribute, check out the Projects tab, or issues. 141 | 142 | 143 | ## Contributing 144 | 145 | 1. Fork it ( https://github.com/jwoertink/fez/fork ) 146 | 2. Create your feature branch (git checkout -b my-new-feature) 147 | 3. Commit your changes (git commit -am 'Add some feature') 148 | 4. Push to the branch (git push origin my-new-feature) 149 | 5. Create a new Pull Request 150 | 151 | ## Contributors 152 | 153 | - [jwoertink](https://github.com/jwoertink) Jeremy Woertink - creator, maintainer 154 | - [Other Brave Souls](https://github.com/jwoertink/fez/graphs/contributors) 155 | -------------------------------------------------------------------------------- /shard.yml: -------------------------------------------------------------------------------- 1 | name: fez 2 | version: 0.11.1 3 | description: Fez is a web application generator for Kemal 4 | 5 | authors: 6 | - Jeremy Woertink 7 | 8 | targets: 9 | fez: 10 | main: src/fez.cr 11 | 12 | crystal: ">= 0.36.1, < 2.0.0" 13 | license: MIT 14 | 15 | development_dependencies: 16 | ameba: 17 | github: veelenga/ameba 18 | version: 0.13.4 19 | -------------------------------------------------------------------------------- /spec/application_spec.cr: -------------------------------------------------------------------------------- 1 | require "./spec_helper" 2 | spec_source "application" 3 | 4 | TMP_SAMPLE_DIR = "/tmp/sample_site" 5 | TMP_SAMPLE_APP = "/tmp/sample_app" 6 | 7 | describe Fez::Application do 8 | describe ".build_directory" do 9 | Spec.before_each do 10 | Dir.mkdir_p(TMP_SAMPLE_APP) 11 | end 12 | 13 | Spec.after_each do 14 | FileUtils.rm_r(TMP_SAMPLE_APP) 15 | FileUtils.rm_r(TMP_SAMPLE_DIR) if Dir.exists?(TMP_SAMPLE_DIR) 16 | end 17 | 18 | it "raises an error when the directory exists" do 19 | expect_raises(Exception) do 20 | app = Fez::Application.new("sample_app") 21 | app.build_directory("/tmp") 22 | end 23 | end 24 | 25 | it "creates a new directory in /tmp/sample_app/site" do 26 | app = Fez::Application.new("site") 27 | app.build_directory(TMP_SAMPLE_DIR) 28 | Dir.exists?(TMP_SAMPLE_DIR).should eq true 29 | end 30 | 31 | it "creates an empty .env file on project's root directory" do 32 | app = Fez::Application.new("site") 33 | root_dir_path = TMP_SAMPLE_DIR 34 | env_path = File.join(root_dir_path, ".env") 35 | app.build_directory(root_dir_path) 36 | app.build_project(Fez::DefaultOptions.template.as(String)) 37 | Dir.exists?(TMP_SAMPLE_DIR).should eq true 38 | File.exists?(env_path).should eq true 39 | end 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /spec/cli_spec.cr: -------------------------------------------------------------------------------- 1 | require "./spec_helper" 2 | spec_source "errors", "default_options", "cli" 3 | 4 | describe Fez::CLI do 5 | describe ".build_project" do 6 | it "raises a NameError when the name is invalid" do 7 | expect_raises Fez::Errors::NameError, "You must specify an application name that begins with a letter. View the help menu for more info." do 8 | cli = Fez::CLI.new("123") 9 | cli.build_project 10 | end 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /spec/spec_helper.cr: -------------------------------------------------------------------------------- 1 | require "spec" 2 | require "file_utils" 3 | require "ecr/macros" 4 | require "../src/fez/*" 5 | 6 | # Doing this because the main fez file works more like an executable. 7 | # Each spec should call it's own file individually. 8 | macro spec_source(*filenames) 9 | {% for filename in filenames %} 10 | require "../src/fez/#{ {{filename}} }" 11 | {% end %} 12 | end 13 | -------------------------------------------------------------------------------- /src/fez.cr: -------------------------------------------------------------------------------- 1 | require "./fez/*" 2 | require "option_parser" 3 | 4 | OptionParser.parse do |parser| 5 | parser.banner = "Usage: fez NAME [--] [ARGS]" 6 | 7 | parser.on("-d DIR", "--directory=DIR", "Set the directory where the app will be built") do |dir| 8 | Fez::DefaultOptions.directory = dir 9 | end 10 | 11 | parser.on("-t TEMPLATE", "--template=TEMPLATE", "Select a template. Options are slang, ecr, or api") do |template| 12 | begin 13 | if template.match(/^slang$|^ecr$|^api$/) 14 | Fez::DefaultOptions.template = template 15 | else 16 | raise Fez::Errors::InvalidTemplateError.new(template) 17 | end 18 | rescue ex 19 | puts ex.message 20 | exit(1) 21 | end 22 | end 23 | 24 | parser.on("-v", "--version", "Fez version") { puts "Fez v#{Fez::VERSION}"; exit } 25 | parser.on("-h", "--help", "Show this help") { puts parser; exit } 26 | end 27 | 28 | # Start the application 29 | Fez::CLI.run 30 | -------------------------------------------------------------------------------- /src/fez/application.cr: -------------------------------------------------------------------------------- 1 | module Fez 2 | class Application 3 | ENV = ".env" 4 | 5 | getter name : String 6 | getter directory : String 7 | 8 | # A valid project name should start with a letter. 9 | # This will also ensure later if we need to generate a class or module, we can 10 | def self.valid_name?(project_name) 11 | !!project_name.match(/\A[a-zA-Z]/) 12 | end 13 | 14 | def initialize(application_name : String) 15 | @name = application_name 16 | @directory = "" 17 | end 18 | 19 | # The directory will be the location plus the app name. 20 | # If this folder exists, raise an error so we don't erase it 21 | def build_directory(directory_name : String) 22 | @directory = case directory_name 23 | when "." then @name 24 | else 25 | File.join(directory_name) 26 | end 27 | 28 | if Dir.exists?(@directory) 29 | raise Fez::Errors::DirectoryExistsError.new(@directory) 30 | else 31 | Dir.mkdir_p(@directory) 32 | end 33 | end 34 | 35 | def build_project(template : String) 36 | template_path = "#{__DIR__}/../templates/#{template}" 37 | 38 | # create all directories under template 39 | Dir.glob("#{template_path}/**/*/") do |dir| 40 | new_dir = dir.gsub("#{template_path}/", "") 41 | puts "Creating directory: #{new_dir}" 42 | Dir.mkdir_p("#{@directory}/#{new_dir}") 43 | end 44 | 45 | # copy all files under template 46 | Dir.glob("#{template_path}/**/*") do |file| 47 | if File.file? file 48 | new_file = file.gsub("#{template_path}/", "") 49 | puts "Copying template: #{new_file}" 50 | File.write("#{@directory}/#{new_file}", File.read(file).to_s) 51 | end 52 | end 53 | 54 | # rename {{name}} files 55 | Dir.glob("#{@directory}/**/*{{name}}*") do |name_file| 56 | rename_file = name_file.gsub("{{name}}", @name) 57 | puts "Renaming template: #{rename_file}" 58 | File.rename(name_file, rename_file) 59 | end 60 | 61 | # process _tmpl files 62 | Dir.glob("#{@directory}/**/*_tmpl") do |tmpl_file| 63 | template = File.read(tmpl_file) 64 | template = template.gsub(/{{name}}/, name) 65 | new_file = tmpl_file.gsub("_tmpl", "") 66 | puts "Processing template: #{new_file}" 67 | File.write(new_file, String.build { |io| 68 | io << template 69 | }) 70 | File.delete tmpl_file 71 | end 72 | 73 | create_empty_env 74 | end 75 | 76 | def create_empty_env 77 | env = File.join(@directory, ENV) 78 | File.new(env, "w") unless File.exists?(env) 79 | end 80 | end 81 | end 82 | -------------------------------------------------------------------------------- /src/fez/cli.cr: -------------------------------------------------------------------------------- 1 | module Fez 2 | struct CLI 3 | def self.run(options = ARGV) 4 | raise Fez::Errors::NameError.new unless options.first? 5 | cli = self.new(options.first) 6 | cli.build_project 7 | cli 8 | end 9 | 10 | def initialize(@name : String) 11 | @directory = Fez::DefaultOptions.directory.as(String) 12 | @template = Fez::DefaultOptions.template.as(String) 13 | end 14 | 15 | def build_project 16 | unless Fez::Application.valid_name?(@name) 17 | raise Fez::Errors::NameError.new 18 | end 19 | puts "Building #{@name}" 20 | 21 | new_app = Fez::Application.new(@name) 22 | new_app.build_directory(@directory) 23 | new_app.build_project(@template) 24 | new_app 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /src/fez/default_options.cr: -------------------------------------------------------------------------------- 1 | module Fez 2 | class DefaultOptions 3 | @@directory = "." 4 | @@template = "slang" 5 | 6 | def self.directory 7 | @@directory 8 | end 9 | 10 | def self.directory=(new_dir) 11 | @@directory = new_dir 12 | end 13 | 14 | def self.template 15 | @@template 16 | end 17 | 18 | def self.template=(template) 19 | @@template = template 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /src/fez/errors.cr: -------------------------------------------------------------------------------- 1 | module Fez 2 | module Errors 3 | # Thrown when the name of the project is invalid 4 | class NameError < Exception 5 | def initialize 6 | super("You must specify an application name that begins with a letter. View the help menu for more info.") 7 | end 8 | end 9 | 10 | # Thrown when the project directory already exists 11 | class DirectoryExistsError < Exception 12 | def initialize(directory) 13 | super("Directory #{directory} already exists. Remove before continuing") 14 | end 15 | end 16 | 17 | class InvalidTemplateError < Exception 18 | def initialize(invalid_template) 19 | super("Template #{invalid_template} is not valid. Options are: slang, ecr, or api") 20 | end 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /src/fez/version.cr: -------------------------------------------------------------------------------- 1 | module Fez 2 | VERSION = {{ `shards version "#{__DIR__}"`.chomp.stringify }} 3 | end 4 | -------------------------------------------------------------------------------- /src/templates/api/.env_tmpl: -------------------------------------------------------------------------------- 1 | KEMAL_ENV=development 2 | PORT=3001 3 | -------------------------------------------------------------------------------- /src/templates/api/.gitignore: -------------------------------------------------------------------------------- 1 | # Common Dot files 2 | .DS_Store 3 | .env 4 | 5 | # Crystal stuff to ignore 6 | /doc 7 | /libs 8 | /.crystal 9 | /.shards 10 | /bin/app 11 | 12 | # Ruby stuff to ignore 13 | /.bundle 14 | 15 | # Web things 16 | /public/uploads/* 17 | /public/stylesheets/* 18 | .sass-cache 19 | -------------------------------------------------------------------------------- /src/templates/api/.travis.yml: -------------------------------------------------------------------------------- 1 | language: crystal 2 | -------------------------------------------------------------------------------- /src/templates/api/Capfile: -------------------------------------------------------------------------------- 1 | # Load DSL and set up stages 2 | require "capistrano/setup" 3 | 4 | # Include default deployment tasks 5 | require "capistrano/deploy" 6 | require "capistrano/kemal" 7 | 8 | # Load custom tasks from `tasks/deployment/` if you have any defined 9 | Dir.glob("tasks/deployment/*.rake").each { |r| import r } 10 | 11 | -------------------------------------------------------------------------------- /src/templates/api/Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | gem "capistrano" 4 | gem "capistrano-kemal" 5 | gem "guard" 6 | gem "guard-kemal" 7 | -------------------------------------------------------------------------------- /src/templates/api/Guardfile_tmpl: -------------------------------------------------------------------------------- 1 | # Watches your main kemal app 2 | guard "kemal", path: ".", file: "app.cr" do 3 | watch("src/{{name}}.cr") 4 | end 5 | -------------------------------------------------------------------------------- /src/templates/api/Makefile: -------------------------------------------------------------------------------- 1 | all: install 2 | 3 | # install ruby and crystal dependencies 4 | install: 5 | shards install 6 | bundle install 7 | 8 | # Compiles scripts and styles 9 | assets: 10 | # Compile assets then run app 11 | run: assets 12 | crystal app.cr 13 | 14 | # Boot local console with app pre-loaded 15 | console: 16 | @icr -r ./config 17 | -------------------------------------------------------------------------------- /src/templates/api/README.md_tmpl: -------------------------------------------------------------------------------- 1 | # {{name}} 2 | 3 | TODO: add description 4 | 5 | ## Requirements 6 | 7 | * [Ruby](https://www.ruby-lang.org/) 8 | * [Crystal](https://crystal-lang.org/) 9 | 10 | ## Installation 11 | 12 | ```text 13 | $ git clone 14 | $ cd {{name}} 15 | $ make install 16 | $ make run 17 | ``` 18 | 19 | ## Development 20 | 21 | Using [Guard](https://github.com/guard/guard) for development auto-reload. Just running the `guard` command will watch your app for changes. After you save your changes to one of the files, kemal will auto-reload for you. This includes the main app.cr, views, stylesheets and javascripts. 22 | 23 | ```text 24 | $ guard 25 | ``` 26 | 27 | You can also load a development console with your app pre-loaded by running `make console`. 28 | 29 | ### API Customization 30 | This app uses [kave](https://github.com/jwoertink/kave) to handle doing API customization. Check out the README to learn more. 31 | 32 | ## Deploying 33 | 34 | Now that you have a handle on developing your Kemal app, you will want to deploy it! There's a lot of different methods to deploying an app in to production, and fez takes the [Capistrano](http://capistranorb.com/) appraoch. 35 | 36 | 1. Edit your `config/deploy.rb` file with the necessary changes. 37 | 2. Edit your `config/deploy/production.rb` file with more necessary changes 38 | 3. Run `cap production deploy`. 39 | 4. Pray it all works! 40 | -------------------------------------------------------------------------------- /src/templates/api/app.cr: -------------------------------------------------------------------------------- 1 | require "./config" 2 | 3 | Kemal.config.port = ENV.fetch("PORT", "3000").to_i 4 | 5 | Kemal.run 6 | -------------------------------------------------------------------------------- /src/templates/api/config.cr_tmpl: -------------------------------------------------------------------------------- 1 | require "kemal" 2 | 3 | require "kave" 4 | require "./src/macros/*" 5 | require "./src/{{name}}.cr" 6 | 7 | if File.exists?(".env") 8 | File.read_lines(".env").each do |line| 9 | key, value = line.strip.split "=" 10 | ENV[key] = value 11 | end 12 | end 13 | 14 | Kave.configure do |c| 15 | # use default options 16 | end 17 | 18 | -------------------------------------------------------------------------------- /src/templates/api/config/deploy.rb_tmpl: -------------------------------------------------------------------------------- 1 | # config valid only for current version of Capistrano 2 | lock '3.16.0' 3 | 4 | set :application, '{{name}}' 5 | set :repo_url, 'git@github.com:me/{{name}}.git' 6 | 7 | # Default branch is :master 8 | # ask :branch, `git rev-parse --abbrev-ref HEAD`.chomp 9 | 10 | # Default deploy_to directory is /var/www/{{name}} 11 | set :deploy_to, "/var/www/#{fetch(:application)}" 12 | 13 | # Default value for :scm is :git 14 | # set :scm, :git 15 | 16 | # Default value for :format is :airbrussh. 17 | # set :format, :airbrussh 18 | 19 | # You can configure the Airbrussh format using :format_options. 20 | # These are the defaults. 21 | # set :format_options, command_output: true, log_file: 'log/capistrano.log', color: :auto, truncate: :auto 22 | 23 | # Default value for :pty is false 24 | # set :pty, true 25 | 26 | # Default value for :linked_files is [] 27 | append :linked_files, '.env' 28 | 29 | # Default value for linked_dirs is [] 30 | append :linked_dirs, 'log' 31 | 32 | # Default value for default_env is {} 33 | # set :default_env, { path: "/opt/crystal/bin:$PATH" } 34 | 35 | # Default value for keep_releases is 5 36 | # set :keep_releases, 5 37 | 38 | # Kemal settings 39 | set :kemal_env, 'production' 40 | 41 | after 'deploy:publishing', 'kemal:restart' 42 | -------------------------------------------------------------------------------- /src/templates/api/config/deploy/production.rb: -------------------------------------------------------------------------------- 1 | # server-based syntax 2 | # ====================== 3 | # Defines a single server with a list of roles and multiple properties. 4 | # You can define all roles on a single server, or split them: 5 | 6 | # server 'example.com', user: 'deploy', roles: %w{app db web}, my_property: :my_value 7 | # server 'example.com', user: 'deploy', roles: %w{app web}, other_property: :other_value 8 | # server 'db.example.com', user: 'deploy', roles: %w{db} 9 | 10 | 11 | 12 | # role-based syntax 13 | # ================== 14 | 15 | # Defines a role with one or multiple servers. The primary server in each 16 | # group is considered to be the first unless any hosts have the primary 17 | # property set. Specify the username and a domain or IP for the server. 18 | # Don't use `:all`, it's a meta role. 19 | 20 | # role :app, %w{deploy@example.com}, my_property: :my_value 21 | # role :web, %w{user1@primary.com user2@additional.com}, other_property: :other_value 22 | # role :db, %w{deploy@example.com} 23 | 24 | 25 | 26 | # Configuration 27 | # ============= 28 | # You can set any configuration variable like in config/deploy.rb 29 | # These variables are then only loaded and set in this stage. 30 | # For available Capistrano configuration variables see the documentation page. 31 | # http://capistranorb.com/documentation/getting-started/configuration/ 32 | # Feel free to add new variables to customise your setup. 33 | 34 | 35 | 36 | # Custom SSH Options 37 | # ================== 38 | # You may pass any option but keep in mind that net/ssh understands a 39 | # limited set of options, consult the Net::SSH documentation. 40 | # http://net-ssh.github.io/net-ssh/classes/Net/SSH.html#method-c-start 41 | # 42 | # Global options 43 | # -------------- 44 | # set :ssh_options, { 45 | # keys: %w(/home/rlisowski/.ssh/id_rsa), 46 | # forward_agent: false, 47 | # auth_methods: %w(password) 48 | # } 49 | # 50 | # The server-based syntax can be used to override options: 51 | # ------------------------------------ 52 | # server 'example.com', 53 | # user: 'user_name', 54 | # roles: %w{web app}, 55 | # ssh_options: { 56 | # user: 'user_name', # overrides user setting above 57 | # keys: %w(/home/user_name/.ssh/id_rsa), 58 | # forward_agent: false, 59 | # auth_methods: %w(publickey password) 60 | # # password: 'please use keys' 61 | # } 62 | -------------------------------------------------------------------------------- /src/templates/api/shard.yml_tmpl: -------------------------------------------------------------------------------- 1 | name: {{name}} 2 | version: 0.0.1 3 | 4 | authors: 5 | - Me 6 | 7 | dependencies: 8 | kemal: 9 | github: kemalcr/kemal 10 | branch: master 11 | kave: 12 | github: jwoertink/kave 13 | branch: master 14 | 15 | development_dependencies: 16 | spec-kemal: 17 | github: kemalcr/spec-kemal 18 | branch: master 19 | icr: 20 | github: crystal-community/icr 21 | branch: master 22 | -------------------------------------------------------------------------------- /src/templates/api/spec/kemal_api_spec.cr: -------------------------------------------------------------------------------- 1 | require "./spec_helper" 2 | 3 | describe "v1 test" do 4 | it "returns some json" do 5 | get "/v1/test.json" 6 | response.body.should eq({"it" => "works"}.to_json) 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /src/templates/api/spec/spec_helper.cr: -------------------------------------------------------------------------------- 1 | ENV["KEMAL_ENV"] = "test" 2 | 3 | require "spec" 4 | require "spec-kemal" 5 | 6 | require "../config" 7 | -------------------------------------------------------------------------------- /src/templates/api/src/macros/helper.cr: -------------------------------------------------------------------------------- 1 | # use this file to create helpful macros 2 | -------------------------------------------------------------------------------- /src/templates/api/src/{{name}}.cr: -------------------------------------------------------------------------------- 1 | api("v1") do 2 | # /v1/test.json 3 | get "/test" do 4 | {"it" => "works"}.to_json 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /src/templates/ecr/.env_tmpl: -------------------------------------------------------------------------------- 1 | KEMAL_ENV=development 2 | PORT=3001 3 | -------------------------------------------------------------------------------- /src/templates/ecr/.gitignore: -------------------------------------------------------------------------------- 1 | # Common Dot files 2 | .DS_Store 3 | .env 4 | 5 | # Crystal stuff to ignore 6 | /doc 7 | /libs 8 | /.crystal 9 | /.shards 10 | /bin/app 11 | 12 | # Ruby stuff to ignore 13 | /.bundle 14 | 15 | # Web things 16 | /public/uploads/* 17 | /public/stylesheets/* 18 | .sass-cache 19 | -------------------------------------------------------------------------------- /src/templates/ecr/.travis.yml: -------------------------------------------------------------------------------- 1 | language: crystal 2 | -------------------------------------------------------------------------------- /src/templates/ecr/Capfile: -------------------------------------------------------------------------------- 1 | # Load DSL and set up stages 2 | require "capistrano/setup" 3 | 4 | # Include default deployment tasks 5 | require "capistrano/deploy" 6 | require "capistrano/kemal" 7 | 8 | # Load custom tasks from `tasks/deployment/` if you have any defined 9 | Dir.glob("tasks/deployment/*.rake").each { |r| import r } 10 | 11 | -------------------------------------------------------------------------------- /src/templates/ecr/Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | gem "capistrano" 4 | gem "capistrano-kemal" 5 | gem "guard" 6 | gem "guard-kemal" 7 | 8 | gem "babel-transpiler" 9 | gem "guard-sass" 10 | gem "sass" 11 | -------------------------------------------------------------------------------- /src/templates/ecr/Guardfile_tmpl: -------------------------------------------------------------------------------- 1 | # Watches your main kemal app 2 | guard "kemal", path: ".", file: "app.cr" do 3 | watch("src/{{name}}.cr") 4 | watch(%r{src/.*\.ecr}) 5 | end 6 | 7 | # Watches your sass files 8 | guard "sass", input: "src/assets/styles", output: "public/stylesheets", style: :compressed 9 | 10 | # Watches your es6 files 11 | watch(/src\/assets\/scripts\/.*\.es6$/) { `ruby es2js.rb` } 12 | -------------------------------------------------------------------------------- /src/templates/ecr/Makefile: -------------------------------------------------------------------------------- 1 | all: install 2 | 3 | # install ruby and crystal dependencies 4 | install: 5 | shards install 6 | bundle install 7 | 8 | # Compiles scripts and styles 9 | assets: 10 | sass --update src/assets/styles:public/stylesheets --style compressed 11 | mkdir -p public/javascripts 12 | touch public/javascripts/application.js 13 | ruby es2js.rb 14 | 15 | # Compile assets then run app 16 | run: assets 17 | crystal app.cr 18 | 19 | # Boot local console with app pre-loaded 20 | console: 21 | @icr -r ./config 22 | -------------------------------------------------------------------------------- /src/templates/ecr/README.md_tmpl: -------------------------------------------------------------------------------- 1 | # {{name}} 2 | 3 | TODO: add description 4 | 5 | ## Requirements 6 | 7 | * [Ruby](https://www.ruby-lang.org/) 8 | * [Crystal](https://crystal-lang.org/) 9 | 10 | ## Installation 11 | 12 | ```text 13 | $ git clone 14 | $ cd {{name}} 15 | $ make install 16 | $ make run 17 | ``` 18 | 19 | ## Development 20 | 21 | Using [Guard](https://github.com/guard/guard) for development auto-reload. Just running the `guard` command will watch your app for changes. After you save your changes to one of the files, kemal will auto-reload for you. This includes the main app.cr, views, stylesheets and javascripts. 22 | 23 | ```text 24 | $ guard 25 | ``` 26 | 27 | You can also load a development console with your app pre-loaded by running `make console`. 28 | 29 | ### Stylesheets 30 | 31 | Add your main stylesheet file in to `src/assets/styles`. This file should end in `.sass` or `.scss` like `src/assets/styles/application.scss`. 32 | If you would like to break your styles in to multiple files (i.e. mixins, variables, header, etc...) just throw those files in to the same directory and name them `_whatever_you_want.scss`. Notice the `_` at the beginning. This is so sass doesn't compile separate files for each one. Then just be sure to `@import "whatever_you_want";` so it gets added in when compiled. 33 | 34 | Using `make run` will first compile your styles, and then generate your `public/stylesheets/application.css` file. You can also compile at any time with `make assets`. 35 | 36 | ### Javascripts 37 | 38 | Add your main javascript file in to `src/assets/scripts`. This file should end in `.es6` like `src/assets/scripts/application.es6`. 39 | You can write full ES6 compatible javascript in these, and they will be compiled to cross-browser compatible scripts using [Babel](https://babeljs.io/) when your app is booted, or the `make assets` task is ran. When they are compiled, the source is placed in `public/javascripts/application.js` or whatever you named the script dot js. 40 | 41 | ## Deploying 42 | 43 | Now that you have a handle on developing your Kemal app, you will want to deploy it! There's a lot of different methods to deploying an app in to production, and fez takes the [Capistrano](http://capistranorb.com/) appraoch. 44 | 45 | 1. Edit your `config/deploy.rb` file with the necessary changes. 46 | 2. Edit your `config/deploy/production.rb` file with more necessary changes 47 | 3. Run `cap production deploy`. 48 | 4. Pray it all works! 49 | -------------------------------------------------------------------------------- /src/templates/ecr/app.cr: -------------------------------------------------------------------------------- 1 | require "./config" 2 | 3 | Kemal.config.port = ENV.fetch("PORT", "3000").to_i 4 | 5 | Kemal.run 6 | -------------------------------------------------------------------------------- /src/templates/ecr/config.cr_tmpl: -------------------------------------------------------------------------------- 1 | require "kemal" 2 | 3 | 4 | require "./src/macros/*" 5 | require "./src/{{name}}.cr" 6 | 7 | if File.exists?(".env") 8 | File.read_lines(".env").each do |line| 9 | key, value = line.strip.split "=" 10 | ENV[key] = value 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /src/templates/ecr/config/deploy.rb_tmpl: -------------------------------------------------------------------------------- 1 | # config valid only for current version of Capistrano 2 | lock '3.16.0' 3 | 4 | set :application, '{{name}}' 5 | set :repo_url, 'git@github.com:me/{{name}}.git' 6 | 7 | # Default branch is :master 8 | # ask :branch, `git rev-parse --abbrev-ref HEAD`.chomp 9 | 10 | # Default deploy_to directory is /var/www/{{name}} 11 | set :deploy_to, "/var/www/#{fetch(:application)}" 12 | 13 | # Default value for :scm is :git 14 | # set :scm, :git 15 | 16 | # Default value for :format is :airbrussh. 17 | # set :format, :airbrussh 18 | 19 | # You can configure the Airbrussh format using :format_options. 20 | # These are the defaults. 21 | # set :format_options, command_output: true, log_file: 'log/capistrano.log', color: :auto, truncate: :auto 22 | 23 | # Default value for :pty is false 24 | # set :pty, true 25 | 26 | # Default value for :linked_files is [] 27 | append :linked_files, '.env' 28 | 29 | # Default value for linked_dirs is [] 30 | append :linked_dirs, 'log' 31 | 32 | # Default value for default_env is {} 33 | # set :default_env, { path: "/opt/crystal/bin:$PATH" } 34 | 35 | # Default value for keep_releases is 5 36 | # set :keep_releases, 5 37 | 38 | # Kemal settings 39 | set :kemal_env, 'production' 40 | 41 | after 'deploy:publishing', 'kemal:restart' 42 | -------------------------------------------------------------------------------- /src/templates/ecr/config/deploy/production.rb: -------------------------------------------------------------------------------- 1 | # server-based syntax 2 | # ====================== 3 | # Defines a single server with a list of roles and multiple properties. 4 | # You can define all roles on a single server, or split them: 5 | 6 | # server 'example.com', user: 'deploy', roles: %w{app db web}, my_property: :my_value 7 | # server 'example.com', user: 'deploy', roles: %w{app web}, other_property: :other_value 8 | # server 'db.example.com', user: 'deploy', roles: %w{db} 9 | 10 | 11 | 12 | # role-based syntax 13 | # ================== 14 | 15 | # Defines a role with one or multiple servers. The primary server in each 16 | # group is considered to be the first unless any hosts have the primary 17 | # property set. Specify the username and a domain or IP for the server. 18 | # Don't use `:all`, it's a meta role. 19 | 20 | # role :app, %w{deploy@example.com}, my_property: :my_value 21 | # role :web, %w{user1@primary.com user2@additional.com}, other_property: :other_value 22 | # role :db, %w{deploy@example.com} 23 | 24 | 25 | 26 | # Configuration 27 | # ============= 28 | # You can set any configuration variable like in config/deploy.rb 29 | # These variables are then only loaded and set in this stage. 30 | # For available Capistrano configuration variables see the documentation page. 31 | # http://capistranorb.com/documentation/getting-started/configuration/ 32 | # Feel free to add new variables to customise your setup. 33 | 34 | 35 | 36 | # Custom SSH Options 37 | # ================== 38 | # You may pass any option but keep in mind that net/ssh understands a 39 | # limited set of options, consult the Net::SSH documentation. 40 | # http://net-ssh.github.io/net-ssh/classes/Net/SSH.html#method-c-start 41 | # 42 | # Global options 43 | # -------------- 44 | # set :ssh_options, { 45 | # keys: %w(/home/rlisowski/.ssh/id_rsa), 46 | # forward_agent: false, 47 | # auth_methods: %w(password) 48 | # } 49 | # 50 | # The server-based syntax can be used to override options: 51 | # ------------------------------------ 52 | # server 'example.com', 53 | # user: 'user_name', 54 | # roles: %w{web app}, 55 | # ssh_options: { 56 | # user: 'user_name', # overrides user setting above 57 | # keys: %w(/home/user_name/.ssh/id_rsa), 58 | # forward_agent: false, 59 | # auth_methods: %w(publickey password) 60 | # # password: 'please use keys' 61 | # } 62 | -------------------------------------------------------------------------------- /src/templates/ecr/es2js.rb: -------------------------------------------------------------------------------- 1 | require "yaml" 2 | require "babel-transpiler" 3 | 4 | manifest = YAML.load_file("src/assets/scripts/manifest.yml") 5 | sources = [] 6 | manifest["files"].each do |filename| 7 | source = File.read("src/assets/scripts/#{filename}") 8 | if File.extname(filename) == ".es6" 9 | source = Babel::Transpiler.transform(source, {'compact' => true, 'comments' => false})["code"] 10 | end 11 | sources << source 12 | end 13 | File.write("public/javascripts/application.js", sources.join("\n")) 14 | -------------------------------------------------------------------------------- /src/templates/ecr/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwoertink/fez/bfc48562261792b0d3c7d8506624f8e5d7e499fb/src/templates/ecr/public/favicon.ico -------------------------------------------------------------------------------- /src/templates/ecr/public/robots.txt: -------------------------------------------------------------------------------- 1 | # Allow or Disallow specific sections of your site from web spiders 2 | # 3 | User-agent: * 4 | -------------------------------------------------------------------------------- /src/templates/ecr/shard.yml_tmpl: -------------------------------------------------------------------------------- 1 | name: {{name}} 2 | version: 0.0.1 3 | 4 | authors: 5 | - Me 6 | 7 | dependencies: 8 | kemal: 9 | github: kemalcr/kemal 10 | branch: master 11 | 12 | development_dependencies: 13 | spec-kemal: 14 | github: kemalcr/spec-kemal 15 | branch: master 16 | icr: 17 | github: crystal-community/icr 18 | branch: master 19 | -------------------------------------------------------------------------------- /src/templates/ecr/spec/kemal_ecr_spec.cr: -------------------------------------------------------------------------------- 1 | require "./spec_helper" 2 | 3 | describe "root path" do 4 | it "loads the home page" do 5 | get "/" 6 | response.body.should match(/Welcome/i) 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /src/templates/ecr/spec/spec_helper.cr: -------------------------------------------------------------------------------- 1 | ENV["KEMAL_ENV"] = "test" 2 | 3 | require "spec" 4 | require "spec-kemal" 5 | 6 | require "../config" 7 | -------------------------------------------------------------------------------- /src/templates/ecr/src/assets/scripts/manifest.yml: -------------------------------------------------------------------------------- 1 | # Add your scripts in the order they need to be included 2 | # They will be compiled in to a single application.js file 3 | files: 4 | - site.es6 5 | -------------------------------------------------------------------------------- /src/templates/ecr/src/assets/scripts/site.es6: -------------------------------------------------------------------------------- 1 | let logger = msg => { window.console.log(msg); } 2 | -------------------------------------------------------------------------------- /src/templates/ecr/src/assets/styles/site.scss: -------------------------------------------------------------------------------- 1 | $black: #111111; 2 | 3 | body { 4 | color: $black; 5 | } 6 | -------------------------------------------------------------------------------- /src/templates/ecr/src/macros/helper.cr: -------------------------------------------------------------------------------- 1 | # view macro lets you specify the view to be displayed 2 | # view("site/index") #=> renders src/views/site/index.ecr with the src/views/layouts/layout.ecr 3 | # view("site/_form") => renders src/views/site/_form.ecr with no layout 4 | # This can be used in a view, or in a route block 5 | macro view(path) 6 | {% if path.split("/").last.starts_with?('_') %} 7 | render "#{__DIR__}/views/#{{{path}}}.ecr" 8 | {% else %} 9 | render "#{__DIR__}/views/#{{{path}}}.ecr", "#{__DIR__}/views/layouts/layout.ecr" 10 | {% end %} 11 | end 12 | -------------------------------------------------------------------------------- /src/templates/ecr/src/views/layouts/layout.ecr_tmpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{name}} 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | <%= content %> 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/templates/ecr/src/views/site/index.ecr: -------------------------------------------------------------------------------- 1 |

Welcome

-------------------------------------------------------------------------------- /src/templates/ecr/src/{{name}}.cr: -------------------------------------------------------------------------------- 1 | get "/" do 2 | view("site/index") 3 | end 4 | -------------------------------------------------------------------------------- /src/templates/slang/.env_tmpl: -------------------------------------------------------------------------------- 1 | KEMAL_ENV=development 2 | PORT=3001 3 | -------------------------------------------------------------------------------- /src/templates/slang/.gitignore: -------------------------------------------------------------------------------- 1 | # Common Dot files 2 | .DS_Store 3 | .env 4 | 5 | # Crystal stuff to ignore 6 | /doc 7 | /libs 8 | /.crystal 9 | /.shards 10 | /bin/app 11 | 12 | # Ruby stuff to ignore 13 | /.bundle 14 | 15 | # Web things 16 | /public/uploads/* 17 | /public/stylesheets/* 18 | .sass-cache 19 | -------------------------------------------------------------------------------- /src/templates/slang/.travis.yml: -------------------------------------------------------------------------------- 1 | language: crystal 2 | -------------------------------------------------------------------------------- /src/templates/slang/Capfile: -------------------------------------------------------------------------------- 1 | # Load DSL and set up stages 2 | require "capistrano/setup" 3 | 4 | # Include default deployment tasks 5 | require "capistrano/deploy" 6 | require "capistrano/kemal" 7 | 8 | # Load custom tasks from `tasks/deployment/` if you have any defined 9 | Dir.glob("tasks/deployment/*.rake").each { |r| import r } 10 | 11 | -------------------------------------------------------------------------------- /src/templates/slang/Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | gem "capistrano" 4 | gem "capistrano-kemal" 5 | gem "guard" 6 | gem "guard-kemal" 7 | 8 | gem "babel-transpiler" 9 | gem "guard-sass" 10 | gem "sass" 11 | -------------------------------------------------------------------------------- /src/templates/slang/Guardfile_tmpl: -------------------------------------------------------------------------------- 1 | # Watches your main kemal app 2 | guard "kemal", path: ".", file: "app.cr" do 3 | watch("src/{{name}}.cr") 4 | watch(%r{src/.*\.slang}) 5 | end 6 | 7 | # Watches your sass files 8 | guard "sass", input: "src/assets/styles", output: "public/stylesheets", style: :compressed 9 | 10 | # Watches your es6 files 11 | watch(/src\/assets\/scripts\/.*\.es6$/) { `ruby es2js.rb` } 12 | -------------------------------------------------------------------------------- /src/templates/slang/Makefile: -------------------------------------------------------------------------------- 1 | all: install 2 | 3 | # install ruby and crystal dependencies 4 | install: 5 | shards install 6 | bundle install 7 | 8 | # Compiles scripts and styles 9 | assets: 10 | sass --update src/assets/styles:public/stylesheets --style compressed 11 | mkdir -p public/javascripts 12 | touch public/javascripts/application.js 13 | ruby es2js.rb 14 | 15 | # Compile assets then run app 16 | run: assets 17 | crystal app.cr 18 | 19 | # Boot local console with app pre-loaded 20 | console: 21 | @icr -r ./config 22 | -------------------------------------------------------------------------------- /src/templates/slang/README.md_tmpl: -------------------------------------------------------------------------------- 1 | # {{name}} 2 | 3 | TODO: add description 4 | 5 | ## Requirements 6 | 7 | * [Ruby](https://www.ruby-lang.org/) 8 | * [Crystal](https://crystal-lang.org/) 9 | 10 | ## Installation 11 | 12 | ```text 13 | $ git clone 14 | $ cd {{name}} 15 | $ make install 16 | $ make run 17 | ``` 18 | 19 | ## Development 20 | 21 | Using [Guard](https://github.com/guard/guard) for development auto-reload. Just running the `guard` command will watch your app for changes. After you save your changes to one of the files, kemal will auto-reload for you. This includes the main app.cr, views, stylesheets and javascripts. 22 | 23 | ```text 24 | $ guard 25 | ``` 26 | 27 | You can also load a development console with your app pre-loaded by running `make console`. 28 | 29 | ### Stylesheets 30 | 31 | Add your main stylesheet file in to `src/assets/styles`. This file should end in `.sass` or `.scss` like `src/assets/styles/application.scss`. 32 | If you would like to break your styles in to multiple files (i.e. mixins, variables, header, etc...) just throw those files in to the same directory and name them `_whatever_you_want.scss`. Notice the `_` at the beginning. This is so sass doesn't compile separate files for each one. Then just be sure to `@import "whatever_you_want";` so it gets added in when compiled. 33 | 34 | Using `make run` will first compile your styles, and then generate your `public/stylesheets/application.css` file. You can also compile at any time with `make assets`. 35 | 36 | ### Javascripts 37 | 38 | Add your main javascript file in to `src/assets/scripts`. This file should end in `.es6` like `src/assets/scripts/application.es6`. 39 | You can write full ES6 compatible javascript in these, and they will be compiled to cross-browser compatible scripts using [Babel](https://babeljs.io/) when your app is booted, or the `make assets` task is ran. When they are compiled, the source is placed in `public/javascripts/application.js` or whatever you named the script dot js. 40 | 41 | ## Deploying 42 | 43 | Now that you have a handle on developing your Kemal app, you will want to deploy it! There's a lot of different methods to deploying an app in to production, and fez takes the [Capistrano](http://capistranorb.com/) appraoch. 44 | 45 | 1. Edit your `config/deploy.rb` file with the necessary changes. 46 | 2. Edit your `config/deploy/production.rb` file with more necessary changes 47 | 3. Run `cap production deploy`. 48 | 4. Pray it all works! 49 | -------------------------------------------------------------------------------- /src/templates/slang/app.cr: -------------------------------------------------------------------------------- 1 | require "./config" 2 | 3 | Kemal.config.port = ENV.fetch("PORT", "3000").to_i 4 | 5 | Kemal.run 6 | -------------------------------------------------------------------------------- /src/templates/slang/config.cr_tmpl: -------------------------------------------------------------------------------- 1 | require "kemal" 2 | require "kilt/slang" 3 | 4 | require "./src/macros/*" 5 | require "./src/{{name}}.cr" 6 | 7 | if File.exists?(".env") 8 | File.read_lines(".env").each do |line| 9 | key, value = line.strip.split "=" 10 | ENV[key] = value 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /src/templates/slang/config/deploy.rb_tmpl: -------------------------------------------------------------------------------- 1 | # config valid only for current version of Capistrano 2 | lock '3.16.0' 3 | 4 | set :application, '{{name}}' 5 | set :repo_url, 'git@github.com:me/{{name}}.git' 6 | 7 | # Default branch is :master 8 | # ask :branch, `git rev-parse --abbrev-ref HEAD`.chomp 9 | 10 | # Default deploy_to directory is /var/www/{{name}} 11 | set :deploy_to, "/var/www/#{fetch(:application)}" 12 | 13 | # Default value for :scm is :git 14 | # set :scm, :git 15 | 16 | # Default value for :format is :airbrussh. 17 | # set :format, :airbrussh 18 | 19 | # You can configure the Airbrussh format using :format_options. 20 | # These are the defaults. 21 | # set :format_options, command_output: true, log_file: 'log/capistrano.log', color: :auto, truncate: :auto 22 | 23 | # Default value for :pty is false 24 | # set :pty, true 25 | 26 | # Default value for :linked_files is [] 27 | append :linked_files, '.env' 28 | 29 | # Default value for linked_dirs is [] 30 | append :linked_dirs, 'log' 31 | 32 | # Default value for default_env is {} 33 | # set :default_env, { path: "/opt/crystal/bin:$PATH" } 34 | 35 | # Default value for keep_releases is 5 36 | # set :keep_releases, 5 37 | 38 | # Kemal settings 39 | set :kemal_env, 'production' 40 | 41 | after 'deploy:publishing', 'kemal:restart' 42 | -------------------------------------------------------------------------------- /src/templates/slang/config/deploy/production.rb: -------------------------------------------------------------------------------- 1 | # server-based syntax 2 | # ====================== 3 | # Defines a single server with a list of roles and multiple properties. 4 | # You can define all roles on a single server, or split them: 5 | 6 | # server 'example.com', user: 'deploy', roles: %w{app db web}, my_property: :my_value 7 | # server 'example.com', user: 'deploy', roles: %w{app web}, other_property: :other_value 8 | # server 'db.example.com', user: 'deploy', roles: %w{db} 9 | 10 | 11 | 12 | # role-based syntax 13 | # ================== 14 | 15 | # Defines a role with one or multiple servers. The primary server in each 16 | # group is considered to be the first unless any hosts have the primary 17 | # property set. Specify the username and a domain or IP for the server. 18 | # Don't use `:all`, it's a meta role. 19 | 20 | # role :app, %w{deploy@example.com}, my_property: :my_value 21 | # role :web, %w{user1@primary.com user2@additional.com}, other_property: :other_value 22 | # role :db, %w{deploy@example.com} 23 | 24 | 25 | 26 | # Configuration 27 | # ============= 28 | # You can set any configuration variable like in config/deploy.rb 29 | # These variables are then only loaded and set in this stage. 30 | # For available Capistrano configuration variables see the documentation page. 31 | # http://capistranorb.com/documentation/getting-started/configuration/ 32 | # Feel free to add new variables to customise your setup. 33 | 34 | 35 | 36 | # Custom SSH Options 37 | # ================== 38 | # You may pass any option but keep in mind that net/ssh understands a 39 | # limited set of options, consult the Net::SSH documentation. 40 | # http://net-ssh.github.io/net-ssh/classes/Net/SSH.html#method-c-start 41 | # 42 | # Global options 43 | # -------------- 44 | # set :ssh_options, { 45 | # keys: %w(/home/rlisowski/.ssh/id_rsa), 46 | # forward_agent: false, 47 | # auth_methods: %w(password) 48 | # } 49 | # 50 | # The server-based syntax can be used to override options: 51 | # ------------------------------------ 52 | # server 'example.com', 53 | # user: 'user_name', 54 | # roles: %w{web app}, 55 | # ssh_options: { 56 | # user: 'user_name', # overrides user setting above 57 | # keys: %w(/home/user_name/.ssh/id_rsa), 58 | # forward_agent: false, 59 | # auth_methods: %w(publickey password) 60 | # # password: 'please use keys' 61 | # } 62 | -------------------------------------------------------------------------------- /src/templates/slang/es2js.rb: -------------------------------------------------------------------------------- 1 | require "yaml" 2 | require "babel-transpiler" 3 | 4 | manifest = YAML.load_file("src/assets/scripts/manifest.yml") 5 | sources = [] 6 | manifest["files"].each do |filename| 7 | source = File.read("src/assets/scripts/#{filename}") 8 | if File.extname(filename) == ".es6" 9 | source = Babel::Transpiler.transform(source, {'compact' => true, 'comments' => false})["code"] 10 | end 11 | sources << source 12 | end 13 | File.write("public/javascripts/application.js", sources.join("\n")) 14 | -------------------------------------------------------------------------------- /src/templates/slang/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwoertink/fez/bfc48562261792b0d3c7d8506624f8e5d7e499fb/src/templates/slang/public/favicon.ico -------------------------------------------------------------------------------- /src/templates/slang/public/robots.txt: -------------------------------------------------------------------------------- 1 | # Allow or Disallow specific sections of your site from web spiders 2 | # 3 | User-agent: * 4 | -------------------------------------------------------------------------------- /src/templates/slang/shard.yml_tmpl: -------------------------------------------------------------------------------- 1 | name: {{name}} 2 | version: 0.0.1 3 | 4 | authors: 5 | - Me 6 | 7 | dependencies: 8 | kemal: 9 | github: kemalcr/kemal 10 | branch: master 11 | slang: 12 | github: jeromegn/slang 13 | branch: master 14 | 15 | development_dependencies: 16 | spec-kemal: 17 | github: kemalcr/spec-kemal 18 | branch: master 19 | icr: 20 | github: crystal-community/icr 21 | branch: master 22 | -------------------------------------------------------------------------------- /src/templates/slang/spec/kemal_slang_spec.cr: -------------------------------------------------------------------------------- 1 | require "./spec_helper" 2 | 3 | describe "root path" do 4 | it "loads the home page" do 5 | get "/" 6 | response.body.should match(/Welcome/i) 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /src/templates/slang/spec/spec_helper.cr: -------------------------------------------------------------------------------- 1 | ENV["KEMAL_ENV"] = "test" 2 | 3 | require "spec" 4 | require "spec-kemal" 5 | 6 | require "kilt/slang" 7 | 8 | require "../config" 9 | -------------------------------------------------------------------------------- /src/templates/slang/src/assets/scripts/manifest.yml: -------------------------------------------------------------------------------- 1 | # Add your scripts in the order they need to be included 2 | # They will be compiled in to a single application.js file 3 | files: 4 | - site.es6 5 | -------------------------------------------------------------------------------- /src/templates/slang/src/assets/scripts/site.es6: -------------------------------------------------------------------------------- 1 | let logger = msg => { window.console.log(msg); } 2 | -------------------------------------------------------------------------------- /src/templates/slang/src/assets/styles/site.scss: -------------------------------------------------------------------------------- 1 | $black: #111111; 2 | 3 | body { 4 | color: $black; 5 | } 6 | -------------------------------------------------------------------------------- /src/templates/slang/src/macros/helper.cr: -------------------------------------------------------------------------------- 1 | # view macro lets you specify the view to be displayed 2 | # view("site/index") #=> renders src/views/site/index.slang with the src/views/layouts/layout.slang 3 | # view("site/_form") => renders src/views/site/_form.slang with no layout 4 | # This can be used in a view, or in a route block 5 | macro view(path) 6 | {% if path.split("/").last.starts_with?('_') %} 7 | render "#{__DIR__}/views/#{{{path}}}.slang" 8 | {% else %} 9 | render "#{__DIR__}/views/#{{{path}}}.slang", "#{__DIR__}/views/layouts/layout.slang" 10 | {% end %} 11 | end 12 | -------------------------------------------------------------------------------- /src/templates/slang/src/views/layouts/layout.slang_tmpl: -------------------------------------------------------------------------------- 1 | doctype html 2 | html 3 | head 4 | title {{name}} 5 | meta charset="utf-8" 6 | meta http-equiv="X-UA-Compatible" content="IE=edge" 7 | meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" 8 | meta name="mobile-web-app-capable" content="yes" 9 | meta name="apple-mobile-web-app-capable" content="yes" 10 | meta name="apple-mobile-web-app-status-bar-style" content="black" 11 | link rel="stylesheet" type="text/css" href="/stylesheets/site.css" 12 | 13 | body 14 | == content 15 | script type="text/javascript" src="/javascripts/application.js" 16 | -------------------------------------------------------------------------------- /src/templates/slang/src/views/site/index.slang: -------------------------------------------------------------------------------- 1 | h1 Welcome -------------------------------------------------------------------------------- /src/templates/slang/src/{{name}}.cr: -------------------------------------------------------------------------------- 1 | get "/" do 2 | view("site/index") 3 | end 4 | --------------------------------------------------------------------------------