README.md in angelo-0.1.24 vs README.md in angelo-0.2.0

- old
+ new

@@ -24,20 +24,25 @@ Celluloid::IO and gives you a reactor with evented IO in Ruby!** Note: There currently is no "standalone" capability where one can define route handlers at the top level. Things will feel very familiar to anyone experienced with Sinatra. Inside the subclass, you can define -route handlers denoted by HTTP verb and path. Unlike Sinatra, the only acceptable return value from a -route block is the body of the response in full. Chunked response support was recently added to Reel, -and look for that support in Angelo soon. +route handlers denoted by HTTP verb and path. One acceptable return value from a route block is the body +of the response in full as a `String`. Another is a `Hash` if the content type is set to `:json`. Finally, +you may return any object that responds to `#each(&block)` if the transfer encoding is set to `:chunked`. +There is also a `chunked_response` helper that will take a block, set the transfer encoding, and return +an appropriate object for you. -Angelo also features `before` and `after` blocks, just like Sinatra. The one difference lies in how -after blocks are handled. See the Errors section below for more info. - There is also [Mustermann](#mustermann) support for full-on, Sinatra-like path matching and params. +Angelo also features `before` and `after` blocks, just like Sinatra. Filters are ordered as defined, +and called in that order. When defined without a path, they run for all matched requests. With a path, +the path must match exactly for the block to be called. If `Angelo::Mustermann` is included, the paths +are interpreted as a Mustermann pattern and params are merged. For more info on the difference in how +after blocks are handled, see the Errors section below for more info. + ### Websockets! One of the main motivations for Angelo was the ability to define websocket handlers with ease. Through the addition of a `websocket` route builder and a `websockets` helper, Angelo attempts to make it easy for you to build real-time web applications. @@ -46,11 +51,11 @@ The `websocket` route builder accepts a path and a block, and passes the actual websocket to the block as the only argument. This socket is an instance of Reel's [WebSocket](https://github.com/celluloid/reel/blob/master/lib/reel/websocket.rb) class, and, as such, responds to methods like `on_message` and `on_close`. A service-wide `on_pong` handler (defined at the -class-level of the Angelo app) is available to customize the behavior when a pong frame comes back from + a connected websocket client. ##### `websockets` helper Angelo includes a "stash" helper for connected websockets. One can `<<` a websocket into `websockets` @@ -128,18 +133,27 @@ ``` The `sse_event` helper accepts a normal `String` for the data, but will automatically convert a `Hash` argument to a JSON object. +NOTE: there is a shortcut helper on the actual SSE object itself: + +```ruby +eventsource '/sse' do |sse| + sse.event :foo, some_key: 'blah', other_key: 'boo' + sse.event :close +end +``` + ##### `sse_message` helper The `sse_message` helper behaves exactly the same as `sse_event`, but does not take an event name: ```ruby eventsource '/sse' do |s| - msg = sse_msg some_key: 'blah', other_key: 'boo' - s.write msg + msg = sse_message some_key: 'blah', other_key: 'boo' + s.write msg s.close end ``` The client javascript would need to be altered to use the `EventSource.onmessage` property as well: @@ -147,10 +161,19 @@ ```javascript var sse = new EventSource('/sse'); sse.onmessage = function(e){ console.log("got message!\n" + JSON.parse(e.data)); }; ``` +NOTE: there is a shortcut helper on the actual SSE object itself: + +```ruby +eventsource '/sse' do |sse| + sse.message some_key: 'blah', other_key: 'boo' + sse.event :close +end +``` + ##### `sses` helper Angelo also includes a "stash" helper for SSE connections. One can `<<` a socket into `sses` from inside an `eventsource` handler block. These can also be "later" be iterated over so one can do things like emit a message on every SSE connection when the service receives a POST request. @@ -353,10 +376,71 @@ Content-Length: 18 everything's fine ``` +### [Tilt](https://github.com/rtomayko/tilt) / ERB + +To make `erb` available in route blocks + +1. add `tilt` to your `Gemfile`: `gem 'tilt'` +2. require `angelo/tilt/erb` +3. include `Angelo::Tilt::ERB` in your app + +```ruby +class Foo < Angelo::Base + include Angelo::Tilt::ERB + + @@views = 'some/other/path' # defaults to './views' + + get '/' do + erb :index + end + +end +``` + +### [Mustermann](https://github.com/rkh/mustermann) + +To make routes blocks match path with Mustermann patterns + +1. be using ruby &gt;=2.0.0 +2. add 'mustermann' to to your `Gemfile`: `platform(:ruby_20){ gem 'mustermann' }` +3. require `angelo/mustermann` +4. include `Angelo::Mustermann` in your app + +```ruby +class Foo < Angelo::Base + include Angelo::Tilt::ERB + include Angelo::Mustermann + + get '/:foo/things/:bar' do + + # `params` is merged with the Mustermann object#params hash, so + # a "GET /some/things/are_good?foo=other&bar=are_bad" would have: + # params: { + # 'foo' => 'some', + # 'bar' => 'are_good' + # } + + @foo = params[:foo] + @bar = params[:bar] + erb :index + end + + before '/:fu/things/*' do + + # `params` is merged with the Mustermann object#params hash, as + # parsed with the current request path against this before block's + # pattern. in the route handler, `params[:fu]` is no longer available. + + @fu = params[:fu] + end + +end +``` + ### WORK LEFT TO DO Lots of work left to do! ### Full-ish example @@ -464,64 +548,29 @@ task :in_sec do |sec, msg| sleep sec.to_i msg end -end + # return a chunked response of JSON for 5 seconds + # + get '/chunky_json' do + content_type :json -Foo.run -``` - -### [Tilt](https://github.com/rtomayko/tilt) / ERB - -To make `erb` available in route blocks - -1. add `tilt` to your `Gemfile`: `gem 'tilt'` -2. require `angelo/tilt/erb` -3. include `Angelo::Tilt::ERB` in your app - -```ruby -class Foo < Angelo::Base - include Angelo::Tilt::ERB - - @@views = 'some/other/path' # defaults to './views' - - get '/' do - erb :index + # this helper requires a block that takes one arg, the response + # proc to call with each chunk (i.e. the block that is passed to + # `#each`) + # + chunked_response do |response| + 5.times do + response.call time: Time.now.to_i + sleep 1 + end + end end end -``` -### [Mustermann](https://github.com/rkh/mustermann) - -To make routes blocks match path with Mustermann patterns - -1. be using ruby &gt;=2.0.0 -2. add 'mustermann' to to your `Gemfile`: `platform(:ruby_20){ gem 'mustermann' }` -3. require `angelo/mustermann` -4. include `Angelo::Mustermann` in your app - -```ruby -class Foo < Angelo::Base - include Angelo::Tilt::ERB - include Angelo::Mustermann - - get '/:foo/things/:bar' do - - # `params` is merged with the Mustermann object#params hash, so - # a "GET /some/things/are_good?foo=other&bar=are_bad" would have: - # params: { - # 'foo' => 'some', - # 'bar' => 'are_good' - # } - - @foo = params[:foo] - @bar = params[:bar] - erb :index - end - -end +Foo.run ``` ### Contributing YES, HAVE SOME