Jellyfish
by Lin Jen-Shin (godfat)
DESCRIPTION:
Pico web framework for building API-centric web applications, either
Rack applications or Rack middlewares. Under 200 lines of code.
DESIGN:
- Learn the HTTP way instead of using some pointless helpers
- Learn the Rack way instead of wrapping Rack functionalities, again
- Learn regular expression for routes instead of custom syntax
- Embrace simplicity over convenience
- Don’t make things complicated only for some convenience, but
great convenience, or simply stay simple for simplicity.
FEATURES:
- Minimal
- Simple
- No templates
- No ORM
- No
dup
incall
- Regular expression routes, e.g.
get %r{^/(?<id>\d+)$}
- String routes, e.g.
get '/'
- Custom routes, e.g.
get Matcher.new
- Build for either Rack applications or Rack middlewares
WHY?
Because Sinatra is too complex and inconsistent for me.
REQUIREMENTS:
- Tested with MRI (official CRuby) 1.9.3, Rubinius and JRuby.
INSTALLATION:
gem install jellyfish
SYNOPSIS:
Hello Jellyfish, your lovely config.ru
require 'jellyfish'
class Tank
include Jellyfish
get '/' do
"Jelly Kelly\n"
end
end
use Rack::ContentLength
use Rack::ContentType, 'text/plain'
run Tank.new
Regular expression routes
require 'jellyfish'
class Tank
include Jellyfish
get %r{^/(?<id>\d+)$} do |match|
"Jelly ##{match[:id]}\n"
end
end
use Rack::ContentLength
use Rack::ContentType, 'text/plain'
run Tank.new
Custom matcher routes
require 'jellyfish'
class Tank
include Jellyfish
class Matcher
def match path
path.reverse == 'match/'
end
end
get Matcher.new do |match|
"#{match}\n"
end
end
use Rack::ContentLength
use Rack::ContentType, 'text/plain'
run Tank.new
Different HTTP status and custom headers
require 'jellyfish'
class Tank
include Jellyfish
post '/' do
headers 'X-Jellyfish-Life' => '100'
headers_merge 'X-Jellyfish-Mana' => '200'
body "Jellyfish 100/200\n"
status 201
'return is ignored if body has already been set'
end
end
use Rack::ContentLength
use Rack::ContentType, 'text/plain'
run Tank.new
Redirect helper
require 'jellyfish'
class Tank
include Jellyfish
get '/lookup' do
found "#{env['rack.url_scheme']}://#{env['HTTP_HOST']}/"
end
end
use Rack::ContentLength
use Rack::ContentType, 'text/plain'
run Tank.new
Crash-proof
require 'jellyfish'
class Tank
include Jellyfish
get '/crash' do
raise 'crash'
end
end
use Rack::ContentLength
use Rack::ContentType, 'text/plain'
run Tank.new
Custom error handler
require 'jellyfish'
class Tank
include Jellyfish
handle NameError do |e|
status 403
"No one hears you: #{e.backtrace.first}\n"
end
get '/yell' do
yell
end
end
use Rack::ContentLength
use Rack::ContentType, 'text/plain'
run Tank.new
Custom controller
require 'jellyfish'
class Heater
include Jellyfish
get '/status' do
temperature
end
def controller; Controller; end
class Controller < Jellyfish::Controller
def temperature
"30\u{2103}\n"
end
end
end
use Rack::ContentLength
use Rack::ContentType, 'text/plain'
run Heater.new
Sinatra flavor controller
Currently support:
- Indifferent params
require 'jellyfish'
class Tank
include Jellyfish
def controller; Jellyfish::Sinatra; end
get %r{^/(?<id>\d+)$} do
"Jelly ##{params[:id]}\n"
end
end
use Rack::ContentLength
use Rack::ContentType, 'text/plain'
run Tank.new
Jellyfish as a middleware
require 'jellyfish'
class Heater
include Jellyfish
get '/status' do
"30\u{2103}\n"
end
end
class Tank
include Jellyfish
get '/' do
"Jelly Kelly\n"
end
end
use Rack::ContentLength
use Rack::ContentType, 'text/plain'
use Heater
run Tank.new
One huge tank
require 'jellyfish'
class Heater
include Jellyfish
get '/status' do
"30\u{2103}\n"
end
end
class Tank
include Jellyfish
get '/' do
"Jelly Kelly\n"
end
end
HugeTank = Rack::Builder.new do
use Rack::ContentLength
use Rack::ContentType, 'text/plain'
use Heater
run Tank.new
end
run HugeTank
Raise exceptions
require 'jellyfish'
class Protector
include Jellyfish
handle Exception do |e|
"Protected: #{e}\n"
end
end
class Tank
include Jellyfish
handle_exceptions false # default is true, setting false here would
make
# the outside Protector handle the exception
get '/' do
raise "Oops, tank broken"
end
end
use Rack::ContentLength
use Rack::ContentType, 'text/plain'
use Protector
run Tank.new
Chunked transfer encoding (streaming)
You would need a proper server setup.
Here’s an example with Rainbows and fibers:
class Tank
include Jellyfish
class Body
def each
(0..4).each{ |i| yield "#{i}\n"; Rainbows.sleep(0.1) }
end
end
get '/chunked' do
Body.new
end
end
use Rack::Chunked
use Rack::ContentType, 'text/plain'
run Tank.new
CONTRIBUTORS:
- Lin Jen-Shin (@godfat)
LICENSE:
Apache License 2.0
Copyright (c) 2012, Lin Jen-Shin (godfat)
Licensed under the Apache License, Version 2.0 (the “License”);
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an “AS IS” BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied.
See the License for the specific language governing permissions and
limitations under the License.
CHANGES:
Jellyfish 0.5.0 – 2012-10-18
Incompatible changes
- Some internal constants are removed.
- Renamed
Respond
toResponse
.
Enhancements
-
Now Jellyfish would always use the custom error handler to handle
the
particular exception even ifhandle_exceptions
set to false. That
is,
now settinghandle_exceptions
to false would only disable default
error handling. This behavior makes more sense since if you want the
exception bubble out then you shouldn’t define the custom error
handler
in the first place. If you define it, you must mean you want to use
it. -
Eliminated some uninitialized instance variable warnings.
-
Now you can access the original app via
jellyfish
in the
controller. -
Jellyfish::Controller
no longer includesJellyfish
, which would
remove
thoseDSL
methods accidentally included in previous version
(0.4.0-).