• Stars
    star
    367
  • Rank 116,257 (Top 3 %)
  • Language
    Ruby
  • Created about 13 years ago
  • Updated about 11 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

An experimental web framework.

Raptor Build Status Dependency Status

https://github.com/garybernhardt/raptor

DESCRIPTION

Raptor is an experimental web framework that encourages simple, decoupled objects. There are no base classes and as little "DSL" as possible. Raptor is not MVC; at least, not in the way that frameworks like Rails are. An example would be handy right about now:

module MyApp
  App = Raptor::App.new(self) do
    path "article" do
      show
      index
      update :if => :admin, :redirect => :index
    end
  end

  module Records
    class Article < YourFavoriteORM::Record
      # Do as you please
    end
  end

  module Constraints
    class Admin
      def match?(params)
        Records::User.find_by_id(params[:user_id]).admin?
      end
    end
  end

  module Presenters
    class Article
      def initialize(subject); @subject = subject; end
      def slug; @subject.title.to_slug; end
    end
  end
end

The first thing you notice: that's a lot of modules! Yes it is. You can break them into files in whatever way you want, but Raptor does expect this layout once everything is loaded.

The second thing you notice: there's no controller! Yes; this is because controllers are the devil. Instead, Raptor has an extremely powerful router:

Routes

Routes can:

  • delegate requests to objects you create.
  • enforce constraints (like "user must be an admin" in the example above).
  • redirect on success, or on certain exceptions, or both.
  • render views.
  • apply presenters before rendering.

The update route in the above example uses the default update behavior we know and love: the same stuff you've written in a hundred Rails controller actions. This behavior is the default in Raptor, but is completely overridable. Here we've overridden it to redirect to index instead of show, and to only work for admins. The request lifecycle is:

  1. Match PUT "/article/:id".
  2. Enforce the :admin constraint, defined by us in MyApp::Constraints::Admin. If the user isn't an admin, stop. The route doesn't match, even though the verb and path do.
  3. Call MyApp::Records::Article.find_and_update. It takes id and params. Raptor's dependency injector notices this, extracts the ID from the URL, as well as the params from the request, and passes them in.
  4. Redirect to /article, the index path. By default it would've gone to the show path, but we overrode it.

In addition, if find_and_update raised Raptor::ValidationError, it would've redirected to :edit. If a template had been rendered, it would've gone through MyApp::Presenters::Article. The full expansion of the update route is:

route :update, "PUT", "article/:id",
  :to => "MyApp::Records::Article.find_and_update",
  :redirect => :show, ValidationError => :edit`

All of the standard Raptor routes are syntactic sugar for these longer forms.

Application structure

By default, the router delegates to records. Records are not to contain application logic; delegation directly to records is only acceptable for very simple operations. For anything complex, it's your job to create relevant objects and point the router at them. Raptor provides you niceties related to the web: routing, presentation, template rendering, etc., but the core of your application—the logic—should have its own design that Raptor can't know ahead of time.

Raptor currently has no database layer. The records themselves are your job. As long as they have methods that match Raptor's interface, you'll be fine.

Rack apps and serving

An application is just a Ruby script:

#!/usr/bin/env ruby
require 'article'
App = Raptor::App.new(MyApp)

App is now a Rack app, so you can create a standard config.ru:

require './app'
run App

and run the app with rackup:

$ rackup

There's no autoloader and no discovery of your code: you explicitly require your source, give your app module to Raptor, and get a Rack app back.

Complex behavior and the injector

Any method that Raptor calls will be injected: subjects, presenters, requirements, even other injectables. Injection is purely name-based: if you have a method named request, it will get the Rack request as an argument. It's your job not to ask for HTTP data in deep layers of your application, like records (unless you really want to, in which case you can, but you should at least feel guilty about it).

Injection is how form parameters are handled, for example. If your route delegates to PostCreator.create(params), Raptor will automatically inject the request params as an argument. You can do the stuff you'd do in a Rails controller without hard coupling yourself to an ActionController::Base class. The reduced coupling makes testing easy and allows reuse (anyone who needs to create a post can use PostCreator!)

To define your own injectables , just define a class:

class MyApp::Injectables::Fruit
  def sources(injector)
    {:watermelon => lambda { "tasty" } }
  end
end

Now, if Raptor calls one of your methods that takes a watermelon argument, it will be passed "tasty".

LICENSE

Released under the MIT license:

More Repositories

1

dotfiles

~grb. Things in here are often interdependent. A lot of stuff relies on scripts in bin/.
Vim Script
1,901
star
2

selecta

A fuzzy text selector for files and anything else you need to select. Use it from vim, from the command line, or anywhere you can run a shell command.
Ruby
1,345
star
3

base

The universal Base class you've always wanted.
Ruby
436
star
4

readygo

A Ruby benchmarking tool accurate to sub-nanosecond time scales
Ruby
222
star
5

static-path

TypeScript
206
star
6

destroy-all-software-extras

Extra material for Destroy All Software Screencasts
Ruby
202
star
7

serveit

ServeIt, a synchronous server and rebuilder of static content like blogs, books, READMEs, etc.
Ruby
156
star
8

expecter

Expecter Gadget: better expectations (assertions) for Python.
Python
103
star
9

dingus

A record-then-assert test double library.
Python
98
star
10

do_not_want

(UNMAINTAINED) Do Not Want: Stops ActiveRecord from doing things you probably didn't want
Ruby
83
star
11

sucks-rocks

Does it suck? Or does it rock?
Ruby
58
star
12

pycomplexity

Scripts to show cyclomatic complexity of Python code in Vim and Emacs.
Python
46
star
13

cls

Cls: terse syntax for your classes
Ruby
40
star
14

python-mock-comparison

A comparison of Python's mocking and other test double libraries
Python
27
star
15

rubies

Ruby
24
star
16

blocks

Python
16
star
17

mote

A very experimental spec runner for Python. Beware: it is incomplete and may change drastically.
Python
12
star
18

LuaSpec

An executable Lua specification so I remember what I've learned.
Lua
12
star
19

explicit_import

Explicit imports for Ruby on a per-class/module basis
Ruby
9
star
20

prest

A REST client library for Python extracted sloppily from BitBacker
Python
5
star
21

the-march-of-progress

Find all the progress indicators from running applications. This does not solve an actual problem.
Ruby
3
star