Skip to content

Instantly share code, notes, and snippets.

@elorest
Created May 7, 2017 06:42
Show Gist options
  • Save elorest/6d00f941dc66ff2a03e346ab41ff6c28 to your computer and use it in GitHub Desktop.
Save elorest/6d00f941dc66ff2a03e346ab41ff6c28 to your computer and use it in GitHub Desktop.
require "http"
require "./*"
module Amber::Controller
class Base
include Render
include Redirect
include Callbacks
protected getter request = HTTP::Request.new("GET", "/")
protected getter response = HTTP::Server::Response.new(IO::Memory.new)
protected getter raw_params = HTTP::Params.parse("")
protected getter context : HTTP::Server::Context?
protected getter params : Amber::Validators::Params = Amber::Validators::Params.new(HTTP::Params.parse("t=t"))
def initialize(@context : HTTP::Server::Context)
set_context(@context.not_nil!)
end
def initialize
end
def set_context(@context : HTTP::Server::Context)
@request = context.request
@response = context.response
@raw_params = context.params
@params = Amber::Validators::Params.new(@raw_params)
end
end
end
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
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
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
module Amber::DSL
record Pipeline, pipeline : Pipe::Pipeline do
def plug(pipe)
pipeline.plug pipe
end
end
record Router, router : Pipe::Router, valve : Symbol, scope : String do
RESOURCES = [:get, :post, :put, :patch, :delete, :options, :head, :trace, :connect]
macro route(verb, resource, controller, action)
puts "{{verb.id}} {{resource.id}} {{controller.id}} {{action.id}}"
%controller = {{controller.id}}.new
%handler = ->%controller.{{action.id}}
%verb = {{verb.upcase.id.stringify}}
%wholeproc = ->(context : HTTP::Server::Context, action : Symbol){
controller = {{controller.id}}.new(context)
controller.run_before_filter(:all)
controller.run_before_filter(action)
content = controller.{{ action.id }}
controller.run_after_filter(action)
controller.run_after_filter(:all)
content
}
%route = Amber::Route.new(%verb, {{resource}}, %wholeproc, %controller, %handler, {{action}}, valve, scope)
router.add(%route)
end
{% for verb in RESOURCES %}
macro {{verb.id}}(*args)
route {{verb}}, \{{*args}}
end
{% end %}
# TODO Clean this up
macro resources(path, controller, actions = [:index, :edit, :new, :show, :create, :update, :delete])
{% if actions.includes?(:index) %}
get "{{path.id}}", {{controller}}, :index
{% end %}
{% if actions.includes?(:edit) %}
get "{{path.id}}/:id/edit", {{controller}}, :edit
{% end %}
{% if actions.includes?(:new) %}
get "{{path.id}}/new", {{controller}}, :new
{% end %}
{% if actions.includes?(:show) %}
get "{{path.id}}/:id", {{controller}}, :show
{% end %}
{% if actions.includes?(:create) %}
post "{{path.id}}", {{controller}}, :create
{% end %}
{% if actions.includes?(:update) %}
patch "{{path.id}}/:id", {{controller}}, :update
put "{{path.id}}/:id", {{controller}}, :update
{% end %}
{% if actions.includes?(:delete) %}
delete "{{path.id}}/:id", {{controller}}, :delete
{% end %}
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment