├── .gitignore ├── .guardian.yml ├── .travis.yml ├── CONTRIBUTE.md ├── LICENSE ├── Makefile ├── README.md ├── examples └── hello_world.rb ├── shard.yml ├── spec ├── satriani_spec.cr └── spec_helper.cr └── src ├── satriani.cr └── satriani ├── application.cr ├── route.cr ├── router.cr ├── server.cr └── version.cr /.gitignore: -------------------------------------------------------------------------------- 1 | /doc/ 2 | /libs/ 3 | /.crystal/ 4 | /.shards/ 5 | 6 | 7 | # Libraries don't need dependency lock 8 | # Dependencies will be locked in application that uses them 9 | /shard.lock 10 | 11 | -------------------------------------------------------------------------------- /.guardian.yml: -------------------------------------------------------------------------------- 1 | files: ./**/*.cr 2 | run: crystal spec 3 | --- 4 | files: ./shard.yml 5 | run: crystal deps 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: crystal 2 | crystal: 3 | - latest 4 | - nightly 5 | matrix: 6 | allow_failures: 7 | - crystal: nightly 8 | -------------------------------------------------------------------------------- /CONTRIBUTE.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | 1. Fork it (https://github.com/marceloboeira/satriani/fork) 4 | 2. Create your feature branch (git checkout -b my-awesome-new-feature) 5 | 3. Commit your changes (follwing our commit police) 6 | 4. Push to the branch (git push origin my-new-feature) 7 | 5. Create a new Pull Request 8 | 9 | ## Commit Policy 10 | 11 | We have a very rigid **commit policy**, to contribute please follow these rules: 12 | 13 | ``` 14 | feature: Add hat wobble 15 | ^--^ ^------------^ 16 | | | 17 | | +-> Summary in present tense 18 | | 19 | +-------> Type: chore, doc, feature, fix, refactor, style, or test 20 | ``` 21 | 22 | ### Available tags: 23 | 24 | * **chore**: Add oyster build script 25 | * **doc**: Explain hat wobble 26 | * **feature**: Add beta sequence 27 | * **fix**: Remove broken confirmation message 28 | * **refactor**: Share logic between 4d3d3d3 and flarhgunnstow 29 | * **style**: Convert tabs to spaces 30 | * **test**: Ensure Tayne retains clothing 31 | 32 | > Based on [this](http://seesparkbox.com/foundry/semantic_commit_messages). 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Marcelo Boeira 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 | CRYSTAL_BIN ?= $(shell which crystal) 2 | 3 | test: 4 | $(CRYSTAL_BIN) spec --verbose 5 | 6 | spec: test 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Satriani](https://s-media-cache-ak0.pinimg.com/736x/e8/06/13/e80613c669a5e198803120573f414c00.jpg) 2 | 3 | # :guitar: Satriani [![Build Status](https://travis-ci.org/marceloboeira/satriani.svg?branch=master)](https://travis-ci.org/marceloboeira/satriani) 4 | > Web microframework for Crystal 5 | 6 | ## Installation 7 | 8 | Add this to your application's `shard.yml`: 9 | 10 | ```yaml 11 | dependencies: 12 | satriani: 13 | github: marceloboeira/satriani 14 | ``` 15 | 16 | ## Example 17 | 18 | In the future it will be easier to create your app with a proper DSL, for now: 19 | 20 | ```crystal 21 | require "../src/satriani" 22 | 23 | routes = [ 24 | Satriani::Route.new("/hello-world") do |request| 25 | "hello-world" 26 | end 27 | ] 28 | 29 | Satriani::Application.new(routes).start 30 | ``` 31 | 32 | ## Contributing 33 | 34 | Want to contribute? check [this](CONTRIBUTE.md) first. 35 | -------------------------------------------------------------------------------- /examples/hello_world.rb: -------------------------------------------------------------------------------- 1 | require "../src/satriani" 2 | 3 | routes = [ 4 | Satriani::Route.new("/hello-world") do |request| 5 | "hello-world" 6 | end 7 | ] 8 | 9 | Satriani::Application.new(routes).start 10 | -------------------------------------------------------------------------------- /shard.yml: -------------------------------------------------------------------------------- 1 | name: satriani 2 | version: 0.0.1 3 | 4 | authors: 5 | - Marcelo Boeira 6 | 7 | license: MIT 8 | -------------------------------------------------------------------------------- /spec/satriani_spec.cr: -------------------------------------------------------------------------------- 1 | require "./spec_helper" 2 | 3 | spawn do 4 | routes = [ 5 | Satriani::Route.new("/joe-satriani/always-with-me-always-with-you") {|request| "1987" }, 6 | Satriani::Route.new("/joe-satriani/flying-in-a-blue-dream") {|request| "1989" }, 7 | ] 8 | 9 | Satriani::Application.new(routes).start 10 | end 11 | 12 | # ensure the server has started before connection attempt 13 | sleep 0.001 14 | 15 | describe Satriani do 16 | http = HTTP::Client.new("0.0.0.0", 8000) 17 | 18 | context "with a valid route" do 19 | it "returns success" do 20 | response = http.get("/joe-satriani/always-with-me-always-with-you") 21 | 22 | response.status_code.should eq(200) 23 | end 24 | 25 | it "renders the content" do 26 | response = http.get("/joe-satriani/always-with-me-always-with-you") 27 | 28 | 29 | response.body.should eq("1987") 30 | end 31 | end 32 | 33 | context "with a invalid route" do 34 | it "returns not found" do 35 | response = http.get("/invalid") 36 | 37 | response.status_code.should eq(404) 38 | end 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /spec/spec_helper.cr: -------------------------------------------------------------------------------- 1 | require "spec" 2 | require "../src/satriani" 3 | -------------------------------------------------------------------------------- /src/satriani.cr: -------------------------------------------------------------------------------- 1 | require "http/server" 2 | require "./satriani/*" 3 | 4 | module Satriani 5 | end 6 | -------------------------------------------------------------------------------- /src/satriani/application.cr: -------------------------------------------------------------------------------- 1 | module Satriani 2 | class Application 3 | property handlers : Array(HTTP::Handler) 4 | property routes : Array(Satriani::Route) 5 | 6 | def initialize(routes : Array(Satriani::Route)) 7 | @routes = routes 8 | 9 | @handlers = [] of HTTP::Handler 10 | @handlers << HTTP::LogHandler.new 11 | @handlers << Satriani::Router.new(@routes) 12 | 13 | @server = Satriani::Server.new(@handlers) 14 | end 15 | 16 | def start 17 | @server.listen 18 | end 19 | 20 | def stop 21 | @server.close 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /src/satriani/route.cr: -------------------------------------------------------------------------------- 1 | module Satriani 2 | class Route 3 | getter :path, :block 4 | 5 | def initialize(@path : String, &@block : HTTP::Request -> String); end 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /src/satriani/router.cr: -------------------------------------------------------------------------------- 1 | require "http/server" 2 | 3 | module Satriani 4 | class Router 5 | include HTTP::Handler 6 | 7 | def initialize(@routes : Array(Satriani::Route)); end 8 | 9 | def call(context) 10 | context.response.content_type = "text/html" 11 | 12 | @routes.each do |route| 13 | if route.path == context.request.path 14 | context.response.status_code = 200 15 | output = route.block.call(context.request) 16 | context.response.print(output) 17 | 18 | return context 19 | end 20 | end 21 | 22 | context.response.status_code = 404 23 | 24 | call_next(context) 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /src/satriani/server.cr: -------------------------------------------------------------------------------- 1 | require "http/server" 2 | 3 | module Satriani 4 | class Server < HTTP::Server 5 | def initialize(@handlers : Array(HTTP::Handler)) 6 | super("0.0.0.0", 8000, @handlers) 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /src/satriani/version.cr: -------------------------------------------------------------------------------- 1 | module Satriani 2 | VERSION = "0.0.1" 3 | end 4 | --------------------------------------------------------------------------------