amethyst
Amethyst 
Amethyst is a web framework written in Crystal language. The goals of Amethyst are to be fast and user-friendly as Rails. Note, Amethyst is in very early stage, so a lot of base features are missing yet. However, it works :). For now, next things are implemented:
- class-based controllers with actions
- middleware support
- simple routing For details, see docs below.
Would be glad for any help with contributing.
Installation
Suggested that you installed Crystal 0.7.1 or higher.
git clone https://github.com/Codcore/Amethyst.git
Or add it to Projectfile
of your Crystal project
deps do
github "Codcore/amethyst"
end
You can play with example of simple web-application written with Amethyst at examples
directory:
crystal examples/simple_application.cr
Usage
If you want to load Amethyst in global namespace to be able not to prepend classes with name of modules they are in (for example, Core::BaseController
),you can load all modules into global namespace next way:
require "amethyst/all"
From that moment, you can type Application.new
instead of Core::Application.new
, BaseController
instead Core::BaseController
, etc.)
Controllers
Amethyst controllers are classes, the name of controller must be NameController
,
and it needs to be inherited from BaseController
. Here is an example of simple controller:
require "../src/amethyst"
class IndexController < Core::BaseController
def hello
html "<p>Hello, you're asked a #{request.method} #{request.path}</p> \n
<a href='/bye'>Visit <b>bye</b> action</a>"
end
def bye
html "<p>Bye!We hope you will come back</p>"
end
def actions
add :hello
add :bye
end
end
Controllers describe actions as a methods. Actions have direct access to request and response objects, and other helpers, such as a html
. Controller method actions
is special method that must be provided by each controller of your application.It lets app to know which methods of your contoller are actions, and which aren't. The synopsys is add :action_name
.
Middleware
Middleware are implemented as classes. Middleware class inherits from Core::Middleware::BaseMiddleware
(or, just type BaseMiddleware`` if you prefer
require amethyst/all), and should have the
call``` method. Actually, there are two call methods with different signatures:
def call(request)
end
def call(request, response)
end
The first one will be called when app gets request from server. It accepts Amethyst::Http::Request
as an argument. Last one will be invoked when controller returned response(this happens automatically). It gets Amethyst::Http::Request
and Amethyst::Http::Response
as arguments. Here is an example of middleware that calculates time elapsed between request and response.
class TimeMiddleware < Core::Middleware::BaseMiddleware
# All instance variables have to be initialized here to use them in call methods
def initialize
@t_req = Time.new
@t_res = Time.new
end
def call(request)
@t_req = Time.now
end
# This one will be called when response returned from controller. It accepts both
# Http::Request and Http::Response
def call(request, response)
@t_res = Time.now
response.body += "<hr> Time elapsed: #{(@t_res-@t_req)} seconds"
end
end
Application creating
app = Core::Application.new
# Middleware registering
app.use(TimeMiddleware.new)
You can set a port and app name (defaul port is 8080
, default name is name of file application is in):
app.port = 8080
app.name = "example"
#Routing
Amethyst has Rails-like approach to describe routes. For now, only get()
supported.
It consists of path and string controller_name#action_name
app.routes.draw do |routes|
# map GET "/" to "hello" action of IndexController
get "/", "index#hello"
map GET "/bye" to "bye" action of IndexController
get "/bye", "index#bye"
end
Note, /bye
and /bye/
work slightly different. First matches /bye, /bye/, /bye_something
, second is "strict",
and matches only /bye
and /bye/
. Both not matches /bye/something
.
You can specify params to be captured:
get "/users/:id", "users#show" #(params not works yet)
After you defined a controller, you have to register it in app with app.routes.register(NameController)
where NameController
(CamelCase) is the classname of your controller:
app.routes.register(IndexController)
#Running application
app.serve
Development
Feel free to fork project and make pull-requests. Stick to standart project structure and name conventions:
src/
amethyst/
module1/ #module1 files
class1.cr
...
module1.cr #loads all module1 files into namespace Amethyst::Module1
module2/
class1.cr #describe class Class1 (module, struct, i.e)
...
module2.cr #loads all module2 files into namespace Amethyst::Module2
file_module.cr #module that consists of one file
amethyst.cr #requires module1.cr, module2.cr, file_module.cr
spec/
module1/
class1_spec.cr #specs for Module1::Class
module2/
class2_spec.cr
spec_helper #loads amethyst.cr
examples/ #examples to play with
#don't forget to require "..src/amethyst"
Contributing
- Fork it ( https://github.com/Codcore/amethyst/fork )
- Create your feature branch (git checkout -b my-new-feature)
- Commit your changes (git commit -am 'Add some feature')
- Push to the branch (git push origin my-new-feature)
- Create a new Pull Request
Contributors
- Codcore codcore - creator, maintainer