Created
May 7, 2017 06:39
-
-
Save elorest/71cc20ca190b4d7c9f3e55ae69fe8afc to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require "http" | |
require "logger" | |
require "json" | |
require "colorize" | |
require "secure_random" | |
require "kilt" | |
require "kilt/slang" | |
require "./amber/dsl/*" | |
require "./amber/support/*" | |
require "./amber/**" | |
module Amber | |
class Server | |
property port : Int32 | |
property name : String | |
property env : String | |
property log : Logger | |
property secret : String | |
def self.instance | |
@@instance ||= new | |
end | |
def self.settings | |
instance | |
end | |
def initialize | |
@app_path = __FILE__ | |
@name = "My Awesome App" | |
@port = 8080 | |
@env = "development".colorize(:yellow).to_s | |
@log = ::Logger.new(STDOUT) | |
@log.level = ::Logger::INFO | |
@secret = SecureRandom.hex | |
end | |
def run | |
time = Time.now | |
host = "127.0.0.1" | |
str_host = "http://#{host}:#{port}".colorize(:light_cyan).underline | |
version = "[Amber #{Amber::VERSION}]".colorize(:light_cyan).to_s | |
log.info "#{version} serving application \"#{name}\" at #{str_host}".to_s | |
server = HTTP::Server.new(host, port, handler) | |
Signal::INT.trap do | |
puts "Shutting down Amber" | |
server.close | |
exit | |
end | |
log.info "Server started in #{env}.".to_s | |
log.info "Startup Time #{Time.now - time}\n\n".colorize(:white).to_s | |
server.listen | |
end | |
def config(&block) | |
with self yield self | |
end | |
macro routes(valve, scope = "") | |
router.draw {{valve}}, {{scope}} do | |
{{yield}} | |
end | |
end | |
macro pipeline(valve) | |
handler.build {{valve}} do | |
{{yield}} | |
end | |
end | |
def handler | |
Pipe::Pipeline.instance | |
end | |
private def router | |
Pipe::Router.instance | |
end | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
doctype html | |
html | |
head | |
title Amber_blog using Kemalyst | |
meta charset="utf-8" | |
meta http-equiv="X-UA-Compatible" content="IE=edge" | |
meta name="viewport" content="width=device-width, initial-scale=1" | |
link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" | |
link rel="stylesheet" href="/stylesheets/main.css" | |
body | |
div.masthead | |
div.container | |
nav.nav | |
== render "layouts/_nav.slang" | |
div.row | |
div.col-sm-12.main | |
== content | |
script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js" | |
script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js" | |
script src="/javascripts/main.js" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require "http" | |
module Amber | |
module Pipe | |
# The base class for Amber Pipes. This extension provides a singleton | |
# method and ability to configure each handler. All configurations should | |
# be maintained in the `/config` folder for consistency. | |
class Base | |
include HTTP::Handler | |
# Ability to configure the singleton instance from the class | |
def self.config | |
yield self.instance | |
end | |
# Ability to configure the instance | |
def config | |
yield self | |
end | |
# Execution of this handler. | |
def call(context : HTTP::Server::Context) | |
call_next context | |
end | |
end | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require "../../../spec_helper" | |
module Amber::Controller | |
describe Base do | |
describe "#render" do | |
it "renders html from slang template" do | |
html_output = <<-HTML | |
<h1>Hello World</h1>\n<p>I am glad you came</p> | |
HTML | |
TestController.new.render_template_page.should eq html_output | |
end | |
it "renders html and layout from slang template" do | |
html_output = <<-HTML | |
<html>\n <body>\n <h1>Hello World</h1>\n<p>I am glad you came</p>\n </body>\n</html> | |
HTML | |
TestController.new.render_layout_too.should eq html_output | |
end | |
it "renders html and layout from slang template" do | |
html_output = <<-HTML | |
<html>\n <body>\n <h1>Hello World</h1>\n<p>I am glad you came</p>\n </body>\n</html> | |
HTML | |
TestController.new.render_both_inferred.should eq html_output | |
end | |
end | |
describe "#before_action" do | |
context "registering action filters" do | |
it "registers a before action" do | |
controller = build_controller("") | |
controller.before_filters | |
before_filters = controller.filters[:before] | |
before_filters.size.should eq 5 | |
end | |
it "registers a after action" do | |
controller = build_controller("") | |
controller.after_filters | |
after_filters = controller.filters[:after] | |
after_filters.size.should eq 2 | |
end | |
end | |
context "running filters" do | |
it "runs before filters" do | |
controller = build_controller("") | |
controller.run_before_filter(:index) | |
controller.total.should eq 4 | |
end | |
it "runs after filters" do | |
controller = build_controller("") | |
controller.run_after_filter(:index) | |
controller.total.should eq 2 | |
end | |
end | |
end | |
describe "#redirect_to" do | |
context "when location is a string" do | |
["www.amberio.com", "/world"].each do |location| | |
it "sets the correct response headers" do | |
hello_controller = build_controller("") | |
hello_controller.redirect_to location | |
response = hello_controller.response | |
response.headers["Location"].should eq location | |
end | |
end | |
end | |
context "when location is a Symbol" do | |
context "when is :back" do | |
context "and has a valid referer" do | |
it "sets the correct response headers" do | |
hello_controller = build_controller("/world") | |
hello_controller.redirect_to :back | |
response = hello_controller.response | |
response.headers["Location"].should eq "/world" | |
end | |
end | |
context "and does not have a referer" do | |
it "raisees an error" do | |
hello_controller = build_controller("") | |
expect_raises Exceptions::Controller::Redirect do | |
hello_controller.redirect_to :back | |
end | |
end | |
end | |
end | |
context "when is an action" do | |
hello_controller = build_controller("/world") | |
hello_controller.redirect_to :world | |
response = hello_controller.response | |
response.headers["Location"].should eq "/world" | |
end | |
end | |
end | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module Amber::DSL | |
module Callbacks | |
macro before_action | |
def before_filters : Nil | |
filters.register :before do | |
{{yield}} | |
end | |
end | |
end | |
macro after_action | |
def after_filters : Nil | |
filters.register :after do | |
{{yield}} | |
end | |
end | |
end | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# The Context holds the request and the response objects. The context is | |
# passed to each handler that will read from the request object and build a | |
# response object. Params and Session hash can be accessed from the Context. | |
class HTTP::Server::Context | |
alias ParamTypes = Nil | String | Int64 | Float64 | Bool | Hash(String, JSON::Type) | Array(JSON::Type) | |
# clear the params. | |
def clear_params | |
@params = HTTP::Params.new({} of String => Array(String)) | |
end | |
# params hold all the parameters that may be passed in a request. The | |
# parameters come from either the url or the body via json or form posts. | |
def params | |
@params ||= HTTP::Params.new({} of String => Array(String)) | |
end | |
def clear_files | |
@files = {} of String => UploadedFile | |
end | |
def files | |
@files ||= {} of String => UploadedFile | |
end | |
# clear the session. You can call this to logout a user. | |
def clear_session | |
@session = {} of String => String | |
end | |
# Holds a hash of session variables. This can be used to hold data between | |
# sessions. It's recommended to avoid holding any private data in the | |
# session since this is held in a cookie. Also avoid putting more than 4k | |
# worth of data in the session to avoid slow pageload times. | |
def session | |
@session ||= {} of String => String | |
end | |
# clear the flash messages. | |
def clear_flash | |
@flash = FlashHash.new | |
end | |
# Holds a hash of flash variables. This can be used to hold data between | |
# requests. Once a flash message is read, it is marked for removal. | |
def flash | |
@flash ||= FlashHash.new | |
end | |
# A hash that keeps track if its been accessed | |
class FlashHash < Hash(String, String) | |
def initialize | |
@read = [] of String | |
super | |
end | |
def fetch(key) | |
@read << key | |
super | |
end | |
def each | |
current = @first | |
while current | |
yield({current.key, current.value}) | |
@read << current.key | |
current = current.fore | |
end | |
self | |
end | |
def unread | |
reject { |key, _| !@read.includes? key } | |
end | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require "./*" | |
require "../support/locale_formats" | |
# We are patching the String class and Number struct to extend the predicates | |
# available this will allow to add friendlier methods for validation cases. | |
class String | |
include Amber::Extensions::StringExtension | |
end | |
abstract struct Number | |
include Amber::Extensions::NumberExtension | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module Amber | |
module Pipe | |
# The CORS Handler adds support for Cross Origin Resource Sharing. | |
class CORS < Base | |
property allow_origin, allow_headers, allow_methods, allow_credentials, | |
max_age | |
def self.instance | |
@@instance ||= new | |
end | |
def initialize | |
@allow_origin = "*" | |
@allow_headers = "Accept, Content-Type" | |
@allow_methods = "GET, HEAD, POST, DELETE, OPTIONS, PUT, PATCH" | |
@allow_credentials = false | |
@max_age = 0 | |
end | |
def call(context : HTTP::Server::Context) | |
begin | |
context.response.headers["Access-Control-Allow-Origin"] = allow_origin | |
# TODO: verify the actual origin matches allowed origins. | |
# if requested_origin = context.request.headers["Origin"] | |
# if allow_origins.includes? requested_origin | |
# end | |
# end | |
if allow_credentials | |
context.response.headers["Access-Control-Allow-credentials"] = "true" | |
end | |
if max_age > 0 | |
context.response.headers["Access-Control-Max-Age"] = max_age.to_s | |
end | |
# if asking permission for request method or request headers | |
if context.request.method.downcase == "options" | |
context.response.status_code = 200 | |
response = "" | |
if requested_method = context.request.headers["Access-Control-Request-Method"] | |
if allow_methods.includes? requested_method.strip | |
context.response.headers["Access-Control-Allow-Methods"] = allow_methods | |
else | |
context.response.status_code = 403 | |
response = "Method #{requested_method} not allowed." | |
end | |
end | |
if requested_headers = context.request.headers["Access-Control-Request-Headers"] | |
requested_headers.split(",").each do |requested_header| | |
if allow_headers.includes? requested_header.strip | |
context.response.headers["Access-Control-Allow-Headers"] = allow_headers | |
else | |
context.response.status_code = 403 | |
response = "Headers #{requested_headers} not allowed." | |
end | |
end | |
end | |
context.response.content_type = "text/html; charset=utf-8" | |
context.response.print(response) | |
else | |
call_next(context) | |
end | |
end | |
end | |
end | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Location for your initialization code | |
# {YourApp}/src/config/app.cr | |
# The config file that Amber generates, web/router.cr, will look something like | |
# this one: | |
# The first line requires the framework library. | |
require "amber" | |
require "./**" | |
require "./*" | |
MY_APP_SERVER = Amber::Server.instance | |
# This line represents how you will define your application configuration. | |
MY_APP_SERVER.config do |app| | |
# Server options | |
app_path = __FILE__ # Do not change unless you understand what you are doing. | |
app.name = "Hello World App" # A descriptive name for your app | |
app.port = 4000 # Port you wish your app to run | |
app.env = "development".colorize(:yellow).to_s | |
app.log = ::Logger.new(STDOUT) | |
app.log.level = ::Logger::INFO | |
# Every Amber application needs to define a pipeline set of pipes | |
# each pipeline allow a set of middleware transformations to be applied to | |
# different sets of route, this give you granular control and explicitness | |
# of which transformation to run for each of the app requests. | |
# All api scoped routes will run these transformations | |
pipeline :web do | |
# Plug is the method to use connect a pipe (middleware) | |
# A plug accepts an instance of HTTP::Handler | |
plug Amber::Pipe::Params.new | |
end | |
# All static content will run these transformations | |
pipeline :static do | |
plug HTTP::StaticFileHandler.new "../examples/public", false | |
plug HTTP::CompressHandler.new | |
end | |
routes :web do | |
resources "/posts", PostController, [:index, :show] | |
end | |
# This is how you define the routes for your application | |
# HTTP methods supported [GET, PATCH, POST, PUT, DELETE, OPTIONS] | |
# Read more about HTTP methods here | |
# (HTTP METHODS)[https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html] | |
# routes :static do | |
# # Each route is defined as follow | |
# # verb resource : String, controller : Symbol, action : Symbol | |
# get "/index.html", StaticController, :index | |
# end | |
# | |
# # Routes accepts a pipeline name and a scope a pipeline represents a stach of | |
# # http handlers that will process the current request | |
# routes :web, "/v2" do | |
# # You can also define all resources at once with the resources macro. | |
# # This will define the following routes | |
# # resources path, controller, actions | |
# # resources "/user", UserController, [:index, :show] | |
# # resources "/user", UserController, actions: [:index, :show] | |
# # | |
# # GET /users UserController :index | |
# # GET /users/:id/edit UserController :edit | |
# # GET /users/new UserController :new | |
# # GET /users/:id UserController :show | |
# # POST /users UserController :create | |
# # PATCH /users/:id UserController :update | |
# # PUT /users/:id UserController :update | |
# # DELETE /users/:id UserController :delete | |
# | |
# resources "/hello", HelloController, [:index, :show] | |
# get "/hello/world/:planet", HelloController, :world | |
# get "/hello/template", HelloController, :template | |
# end | |
end | |
# Run the server | |
MY_APP_SERVER.run |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module Amber::Controller | |
module Callbacks | |
macro included | |
include Amber::DSL::Callbacks | |
property filters : Filters = Filters.new | |
def run_before_filter(action) | |
if self.responds_to? :before_filters | |
self.before_filters | |
@filters.run(:before, action) | |
@filters.run(:before, :all) | |
end | |
end | |
def run_after_filter(action) | |
if self.responds_to? :after_filters | |
self.after_filters | |
@filters.run(:after, action) | |
@filters.run(:after, :all) | |
end | |
end | |
end | |
end | |
record Filter, precedence : Symbol, action : Symbol, blk : -> Nil do | |
end | |
# Builds a BeforeAction filter. | |
# | |
# The yielded object has an `only` method that accepts two arguments, | |
# a key (`Symbol`) and a block ->`Nil`. | |
# | |
# ``` | |
# FilterChainBuilder.build do |b| | |
# filter :index, :show { some_method } | |
# filter :delete { } | |
# end | |
# ``` | |
record FilterBuilder, filters : Filters, precedence : Symbol do | |
def only(action : Symbol, &block : -> Nil) | |
add(action, &block) | |
end | |
def only(actions : Array(Symbol), &block : -> Nil) | |
actions.each { |action| add(action, &block) } | |
end | |
def all(&block : -> Nil) | |
filters.add Filter.new(precedence, :all, block) | |
end | |
def add(action, &block : -> Nil) | |
filters.add Filter.new(precedence, action, block) | |
end | |
end | |
class Filters | |
include Enumerable({Symbol, Array(Filter)}) | |
property filters = {} of Symbol => Array(Filter) | |
def register(precedence : Symbol) : Nil | |
with FilterBuilder.new(self, precedence) yield | |
end | |
def add(filter : Filter) | |
filters[filter.precedence] ||= [] of Filter | |
filters[filter.precedence] << filter | |
end | |
def run(precedence : Symbol, action : Symbol) | |
filters[precedence].each do |filter| | |
filter.blk.call if filter.action == action | |
end | |
end | |
def [](name) | |
filters[name] | |
end | |
def []?(name) | |
fetch(name) { nil } | |
end | |
def fetch(name) | |
filters.fetch(name) | |
end | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
div.row | |
div.col-sm-11 | |
h2 Posts | |
div.col-sm-1 | |
a.btn.btn-success.btn-xs href="/posts/new" New | |
div.table-responsive | |
table.table.table-striped | |
thead | |
tr | |
th Title | |
th Content | |
th Actions | |
tbody | |
- posts.each do |post| | |
tr | |
td = post.title | |
td = post.content | |
td | |
span | |
a.btn.btn-primary.btn-xs href="/posts/#{ post.id }" read | |
a.btn.btn-success.btn-xs href="/posts/#{ post.id }/edit" edit | |
a.btn.btn-danger.btn-xs href="/posts/#{ post.id }?_method=delete" onclick="return confirm('Are you sure?');" delete |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
The MIT License (MIT) | |
Copyright (c) 2017 Elias Perez | |
Permission is hereby granted, free of charge, to any person obtaining a copy | |
of this software and associated documentation files (the "Software"), to deal | |
in the Software without restriction, including without limitation the rights | |
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
copies of the Software, and to permit persons to whom the Software is | |
furnished to do so, subject to the following conditions: | |
The above copyright notice and this permission notice shall be included in | |
all copies or substantial portions of the Software. | |
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
THE SOFTWARE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module Amber | |
module Pipe | |
# This class picks the correct pipeline based on the request | |
# and executes it. | |
class Pipeline < Base | |
getter pipeline | |
getter valve : Symbol | |
def self.instance | |
@@instance ||= new | |
end | |
def initialize | |
@router = Pipe::Router.instance | |
@valve = :web | |
@pipeline = {} of Symbol => Array(HTTP::Handler) | |
@pipeline[@valve] = [] of HTTP::Handler | |
end | |
def call(context : HTTP::Server::Context) | |
valve = @router.match_by_request(context.request).payload.valve | |
pipe = proccess_pipeline(@pipeline[valve]) | |
pipe.call(context) | |
context | |
end | |
# Connects pipes to a pipeline to process requests | |
def build(valve : Symbol, &block) | |
@valve = valve | |
@pipeline[@valve] = [] of HTTP::Handler unless @pipeline.key? @valve | |
with DSL::Pipeline.new(self) yield | |
end | |
def plug(pipe : HTTP::Handler) | |
@pipeline[@valve] << pipe | |
end | |
def proccess_pipeline(pipes, last_pipe : (Context ->)? = nil) | |
pipes << Router.instance unless pipes.includes? Router.instance | |
raise ArgumentError.new "You must specify at least one pipeline." if pipes.empty? | |
0.upto(pipes.size - 2) { |i| pipes[i].next = pipes[i + 1] } | |
pipes.last.next = last_pipe if last_pipe | |
pipes.first | |
end | |
end | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require "../models/post" | |
class PostController < Amber::Controller::Base | |
def index | |
# posts = Post.all | |
# # context = context.not_nil! | |
# render("index.slang") | |
"sad too" | |
end | |
def show | |
# if post = Post.find params["id"] | |
# render("show.slang") | |
# else | |
# # context.flash["warning"] = "Post with ID #{params["id"]} Not Found" | |
# redirect "/posts" | |
# end | |
"what sad too" | |
end | |
# def new | |
# post = Post.new | |
# html render("post/new.slang", "main.slang") | |
# end | |
# | |
# def create | |
# post = Post.new(params.to_h.select(["title", "content"])) | |
# | |
# if post.valid? && post.save | |
# context.flash["success"] = "Created Post successfully." | |
# redirect "/posts" | |
# else | |
# context.flash["danger"] = "Could not create Post!" | |
# html render("post/new.slang", "main.slang") | |
# end | |
# end | |
# | |
# def edit | |
# if post = Post.find params["id"] | |
# html render("post/edit.slang", "main.slang") | |
# else | |
# context.flash["warning"] = "Post with ID #{params["id"]} Not Found" | |
# redirect "/posts" | |
# end | |
# end | |
# | |
# def update | |
# if post = Post.find(params["id"]) | |
# post.set_attributes(params.to_h.select(["title", "content"])) | |
# if post.valid? && post.save | |
# context.flash["success"] = "Updated Post successfully." | |
# redirect "/posts" | |
# else | |
# context.flash["danger"] = "Could not update Post!" | |
# html render("post/edit.slang", "main.slang") | |
# end | |
# else | |
# context.flash["warning"] = "Post with ID #{params["id"]} Not Found" | |
# redirect "/posts" | |
# end | |
# end | |
# | |
# def delete | |
# if post = Post.find params["id"] | |
# post.destroy | |
# else | |
# context.flash["warning"] = "Post with ID #{params["id"]} Not Found" | |
# end | |
# redirect "/posts" | |
# end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module Amber::Controller | |
module Render | |
macro render_both(filename, layout) | |
content = render_template("{{filename.id}}") | |
render_template("{{layout.id}}") | |
end | |
# helper to render a template. The view name is relative to `src/views` directory. | |
macro render_template(filename, *args) | |
{% if filename.id.split("/").size > 2 %} | |
Kilt.render("{{filename.id}}", {{*args}}) | |
{% else %} | |
Kilt.render("src/views/{{filename.id}}", {{*args}}) | |
{% end %} | |
end | |
macro render(filename, layout = "layouts/application.slang", path = "src/views", folder = __FILE__) | |
render_both "#{{{path}}}/#{{{folder.split("/").last.gsub(/\_controller\.cr|\.cr/, "")}}}/#{{{filename}}}", "#{{{path}}}/#{{{layout}}}" | |
end | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module Amber | |
class Route | |
property :controller, :handler, :action, :verb, :resource, :valve, :params,:scope | |
property wholeproc : Proc(HTTP::Server::Context, Symbol, String) | |
def initialize(@verb : String, | |
@resource : String, | |
@wholeproc, | |
@controller = Controller::Base.new, | |
@handler : Proc(String) = ->{ "500" }, | |
@action : Symbol = :index, | |
@valve : Symbol = :web, | |
@scope : String = "") | |
end | |
def trail | |
"#{verb.to_s.downcase}#{scope}#{resource}" | |
end | |
def trail_head | |
"head#{scope}#{resource}" | |
end | |
def call(context) | |
# controller.set_context(context) | |
# controller.run_before_filter(:all) | |
# controller.run_before_filter(action) | |
# content = handler.call | |
# controller.run_after_filter(action) | |
# controller.run_after_filter(:all) | |
# content | |
wholeproc.call(context, action) unless wholeproc.is_a?(String) | |
end | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require "radix" | |
module Amber | |
module Pipe | |
class Router < Base | |
property :routes | |
def self.instance | |
@@instance ||= new | |
end | |
def initialize | |
@routes = Radix::Tree(Route).new | |
end | |
def call(context : HTTP::Server::Context) | |
raise Exceptions::RouteNotFound.new(context.request) if !route_defined?(context.request) | |
route_node = match_by_request(context.request) | |
merge_params(route_node.params, context) | |
content = route_node.payload.call(context) | |
ensure | |
context.response.print(content) | |
context | |
end | |
# This registers all the routes for the application | |
def draw(valve : Symbol) | |
with DSL::Router.new(self, valve, "") yield | |
end | |
def draw(valve : Symbol, scope : String) | |
with DSL::Router.new(self, valve, scope) yield | |
end | |
def add(route : Route) | |
trail = build_node(route.verb, route.resource) | |
node = @routes.add(route.trail, route) | |
add_head(route) if route.verb == :GET | |
node | |
rescue Radix::Tree::DuplicateError | |
raise Amber::Exceptions::DuplicateRouteError.new(route) | |
end | |
def route_defined?(request) | |
match_by_request(request).found? | |
end | |
def match_by_request(request) | |
match(request.method, request.path) | |
end | |
private def merge_params(params, context) | |
params.each { |k, v| context.params.add(k.to_s, v) } | |
end | |
private def match(http_verb, resource) : Radix::Result(Amber::Route) | |
@routes.find build_node(http_verb, resource) | |
end | |
private def build_node(http_verb : Symbol | String, resource : String) | |
"#{http_verb.to_s.downcase}#{resource}" | |
end | |
private def add_head(route) | |
@routes.add(route.trail_head, route) | |
end | |
end | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module Amber | |
module Extensions | |
module StringExtension | |
def str? | |
self.is_a? String | |
end | |
# email validation | |
def email? | |
!!self.match(/^[_]*([a-z0-9]+(\.|_*)?)+@([a-z][a-z0-9-]+(\.|-*\.))+[a-z]{2,6}$/) | |
end | |
# domain validation | |
def domain? | |
!!self.match(/^([a-z][a-z0-9-]+(\.|-*\.))+[a-z]{2,6}$/) | |
end | |
# url validation | |
def url? | |
!!self.match(/^(http(s)?(:\/\/))?(www\.)?[a-zA-Z0-9-_\.]+(\.[a-zA-Z0-9]{2,})([-a-zA-Z0-9:%_\+.~#?&\/\/=]*)/) | |
end | |
# ip v4 validation | |
def ipv4? | |
!!self.match(/^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/) | |
end | |
# ip v6 validation | |
def ipv6? | |
!!self.match(/^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|6(?:011|5[0-9][0-9])[0-9]{12}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|(?:2131|1800|35\d{3})\d{11})$/) | |
end | |
# mac address validation | |
def mac_address? | |
!!self.match(/^([0-9a-fA-F][0-9a-fA-F]:){5}([0-9a-fA-F][0-9a-fA-F])$/) | |
end | |
# hex color validation | |
def hex_color? | |
!!self.match(/^#?([0-9A-F]{3}|[0-9A-F]{6})$/i) | |
end | |
# hexadecimal validation | |
def hex? | |
!!self.match(/^(0x)?[0-9A-F]+$/i) | |
end | |
# alpha characters validation | |
def alpha?(locale = "en-US") | |
!!self.match(Support::LocaleFormat::ALPHA[locale]) | |
end | |
# numeric characters validation | |
def numeric? | |
!!self.match(/^([0-9]+)$/) | |
end | |
# alpha numeric characters validation | |
def alphanum?(locale = "en-US") | |
!!self.match(Support::LocaleFormat::ALPHA_NUM[locale]) | |
end | |
# md5 validation | |
def md5? | |
!!self.match(/^[a-f0-9]{32}$/) | |
end | |
# base64 validation | |
def base64? | |
!!self.match(/^[a-zA-Z0-9+\/]+={0,2}$/) && (self.size % 4 === 0) | |
end | |
# slug validation | |
def slug? | |
!!self.match(/^([a-zA-Z0-9_-]+)$/) | |
end | |
# lower case validation | |
def lower? | |
self.downcase === self | |
end | |
# upper case validation | |
def upper? | |
self.upcase === self | |
end | |
# credit card validation | |
def credit_card? | |
!!self.match(/^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|(222[1-9]|22[3-9][0-9]|2[3-6][0-9]{2}|27[01][0-9]|2720)[0-9]{12}|6(?:011|5[0-9][0-9])[0-9]{12}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|(?:2131|1800|35\d{3})\d{11})|62[0-9]{14}$/) | |
end | |
# phone number validation | |
def phone?(locale = "en-US") | |
!!self.match(Support::LocaleFormat::PHONE_FORMAT[locale]) | |
end | |
def excludes?(value) | |
!!self.includes?(value) | |
end | |
# time string validation | |
def time_string? | |
!!self.match(/^(2[0-3]|[01]?[0-9]):([0-5]?[0-9]):([0-5]?[0-9])$/) | |
end | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment