nyny 1.0.0.pre1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,41 @@
1
+ #!ruby -I ../../lib -I lib
2
+
3
+ ENV['RACK_ENV'] ||= 'development'
4
+
5
+ require 'rubygems'
6
+ require 'bundler'
7
+ Bundler.setup(:default, ENV['RACK_ENV'].to_sym)
8
+
9
+ require 'nyny'
10
+ require_relative 'database'
11
+
12
+ TEMPLATE = DATA.read.freeze
13
+
14
+ class App < NYNY::App
15
+ get '/' do
16
+ shouts = Shout.all.reverse
17
+ ERB.new(TEMPLATE).result(binding)
18
+ end
19
+
20
+ post '/shouts' do
21
+ Shout.create :body => params[:body]
22
+ redirect_to '/'
23
+ end
24
+ end
25
+
26
+ App.run! 9000
27
+
28
+ __END__
29
+ <html>
30
+ <body>
31
+ <form action="/shouts" method="post">
32
+ <input type="text" name="body"></input>
33
+ <input type="submit" value="SHOUT"></input>
34
+ </form>
35
+ <ul>
36
+ <% shouts.each do |shout| %>
37
+ <li><%= shout.body %>
38
+ <% end %>
39
+ </ul>
40
+ </body>
41
+ </html>
@@ -1,11 +1,11 @@
1
1
  #!ruby -I ../lib -I lib
2
- require 'frankie'
2
+ require 'nyny'
3
3
  require 'json'
4
4
 
5
5
  #
6
6
  # Every response of this app will be automatically converted to json
7
7
  #
8
- class App < Frankie::App
8
+ class App < NYNY::App
9
9
  before { headers 'Content-Type' => 'application/json' }
10
10
 
11
11
  after do
@@ -1,9 +1,10 @@
1
- #!ruby -I ../lib -I lib
2
- require 'frankie'
1
+ #!ruby -I ../../lib -I lib
2
+ require 'nyny'
3
3
  require 'sinatra'
4
4
  require 'ostruct'
5
5
 
6
6
  module Views
7
+ CACHE = Tilt::Cache.new
7
8
  include ::Sinatra::Templates
8
9
 
9
10
  def settings
@@ -11,12 +12,12 @@ module Views
11
12
  end
12
13
 
13
14
  def template_cache
14
- @template_cache = Tilt::Cache.new
15
+ CACHE
15
16
  end
16
17
  end
17
18
 
18
19
 
19
- class App < Frankie::App
20
+ class App < NYNY::App
20
21
  helpers Views
21
22
 
22
23
  get '/' do
@@ -1,5 +1,5 @@
1
1
  #!ruby -I ../../lib -I lib
2
- require 'frankie'
2
+ require 'nyny'
3
3
  require 'faye/websocket'
4
4
 
5
5
  #
@@ -37,7 +37,7 @@ class WebSockets
37
37
  end
38
38
  end
39
39
 
40
- class App < Frankie::App
40
+ class App < NYNY::App
41
41
  #Serve static assets from public folder
42
42
  use Rack::Static, :urls => ["/public"]
43
43
 
@@ -52,8 +52,8 @@ class App < Frankie::App
52
52
  end
53
53
  end
54
54
 
55
- get '/frankie' do
56
- 'yep, you can still use frankie'
55
+ get '/nyny' do
56
+ 'yep, you can still use nyny'
57
57
  end
58
58
  end
59
59
 
@@ -1,9 +1,11 @@
1
1
  require 'uri'
2
2
  require 'rack'
3
3
 
4
- require "nyny/version"
4
+ require 'nyny/version'
5
5
  require 'nyny/primitives'
6
6
  require 'nyny/request_scope'
7
7
  require 'nyny/route_signature'
8
- require 'nyny/class_level_api'
8
+ require 'nyny/runner'
9
+ require 'nyny/middleware_chain'
10
+ require 'nyny/router'
9
11
  require 'nyny/app'
@@ -1,66 +1,65 @@
1
1
  module NYNY
2
2
  class App
3
- extend ClassLevelApi
4
-
5
- RouteNotFoundError = Class.new StandardError
3
+ HTTP_VERBS = [:delete, :get, :head, :options, :patch, :post, :put, :trace]
4
+ extend Runner
6
5
 
6
+ attr_reader :middleware_chain, :router
7
7
  def initialize app=nil
8
- @app = app || lambda {|env| Response.new '', 404 }
9
- build_middleware_chain
8
+ @router = Router.new({
9
+ :routes => self.class.routes,
10
+ :fallback => (app || lambda {|env| Response.new '', 404 }),
11
+ :before_hooks => self.class.before_hooks,
12
+ :after_hooks => self.class.after_hooks
13
+ })
14
+ @middleware_chain = MiddlewareChain.new(self.class.middlewares,
15
+ lambda {|env| _call(env)})
10
16
  end
11
17
 
12
- def build_middleware_chain
13
- @top = self.class.middlewares.reverse.reduce (self) do |prev, entry|
14
- klass, args, blk = entry
15
- klass.new prev, *args, &blk
16
- end
18
+ def _call env
19
+ router.call env
17
20
  end
18
21
 
19
- def self.run! port=9292
20
- middlewares.unshift Rack::ShowExceptions, Rack::CommonLogger
21
-
22
- begin
23
- Rack::Handler::Thin
24
- rescue LoadError
25
- Rack::Handler::WEBrick
26
- end.run new, :Port => port
22
+ def call env
23
+ middleware_chain.call env
27
24
  end
28
25
 
29
- def handler_for_path method, path
30
- self.class.routes.fetch(method.downcase.to_sym).each do |sig, h|
31
- params = sig.match path
32
- return [h, params] if params
26
+ #class methods
27
+ class << self
28
+ HTTP_VERBS.each do |method|
29
+ define_method method do |str, &blk|
30
+ (routes[method] ||= {})[RouteSignature.new(str)] = Proc.new &blk
31
+ end
33
32
  end
34
33
 
35
- raise RouteNotFoundError
36
- end
34
+ def middlewares; @middlewares ||= [] end
35
+ def routes; @routes ||= {} end
36
+ def before_hooks; @before_hooks ||= [] end
37
+ def after_hooks; @after_hooks ||= [] end
37
38
 
38
- def route req
39
- begin
40
- handler, params = handler_for_path req.request_method, req.path
41
- req.params.merge! params
42
- RequestScope.new(self, req).apply_to &handler
43
- rescue KeyError, RouteNotFoundError
44
- @app.call req.env
39
+ def use_protection! args={}
40
+ begin
41
+ require 'rack/protection'
42
+ middlewares.unshift [Rack::Protection, args]
43
+ rescue LoadError
44
+ puts "WARN: to use protection, you must install 'rack-protection' gem"
45
+ end
45
46
  end
46
- end
47
47
 
48
- def _call env
49
- route Request.new(env)
50
- end
48
+ def before &blk
49
+ before_hooks << Proc.new(&blk)
50
+ end
51
51
 
52
- def call env
53
- if @top == self
54
- _call env
55
- else
56
- if not @initialized_chain
57
- @initialized_chain = true
58
- @top.call(env)
59
- else
60
- @initialized_chain = false
61
- _call env
62
- end
52
+ def after &blk
53
+ after_hooks << Proc.new(&blk)
63
54
  end
64
- end
55
+
56
+ def use middleware, *args, &block
57
+ middlewares << [middleware, args, block]
58
+ end
59
+
60
+ def helpers *args
61
+ args.each {|m| RequestScope.add_helper_module m }
62
+ end
63
+ end #class methods
65
64
  end
66
65
  end
@@ -0,0 +1,14 @@
1
+ module NYNY
2
+ class MiddlewareChain
3
+ def initialize middlewares, proxy
4
+ @top = middlewares.reverse.reduce (proxy) do |prev, entry|
5
+ klass, args, blk = entry
6
+ klass.new prev, *args, &blk
7
+ end
8
+ end
9
+
10
+ def call env
11
+ @top.call(env)
12
+ end
13
+ end
14
+ end
@@ -1,16 +1,15 @@
1
1
  module NYNY
2
2
  class RequestScope
3
- attr_reader :request, :app, :response
3
+ attr_reader :request, :response
4
4
 
5
5
  def self.add_helper_module m
6
6
  include m
7
7
  end
8
8
 
9
- def initialize app, req
10
- @app = app
9
+ def initialize request
11
10
  @headers = {'Content-Type' => 'text/html'}
12
11
  @status = 200
13
- @request = req
12
+ @request = request
14
13
  end
15
14
 
16
15
  def params
@@ -42,17 +41,12 @@ module NYNY
42
41
  end
43
42
 
44
43
  def apply_to &handler
45
- params.default_proc = proc {|h,k| h[k.to_s] || h[k.to_sym]}
46
- app.class.before_hooks.each {|h| instance_eval &h }
47
-
48
44
  @response = @halt_response || begin
49
45
  Response.new instance_eval(&handler), @status, @headers
50
46
  end
51
47
 
52
48
  cookies.each {|k,v| @response.set_cookie k,v }
53
49
  @response.redirect(@redirect) if @redirect
54
-
55
- app.class.after_hooks.each {|h| instance_eval &h }
56
50
  @response.finish
57
51
  @response
58
52
  end
@@ -3,27 +3,27 @@ module NYNY
3
3
  NAME_PATTERN = /:(\S+)/
4
4
 
5
5
  attr_reader :pattern
6
- def initialize route
7
- @pattern = if route.is_a? Regexp
8
- route
9
- else
10
- pattern_for route.dup
11
- end
6
+ def initialize signature
7
+ @pattern = pattern_for signature
12
8
  end
13
9
 
14
10
  def pattern_for string
11
+ return string if string.is_a? Regexp
15
12
  return string unless string.include? ':'
16
- string = "/#{string}" unless string.start_with? '/'
17
- parts = string.split '/'
18
13
 
19
- groups = parts.map do |part|
14
+ signature = string.start_with?('/') ? string : "/#{string}"
15
+ build_regex signature
16
+ end
17
+
18
+ def build_regex signature
19
+ groups = signature.split('/').map do |part|
20
20
  next part if part.empty?
21
21
  next part unless part.start_with? ':'
22
22
  name = NAME_PATTERN.match(part)[1]
23
23
  %Q{(?<#{name}>\\S+)}
24
- end.select {|s| !s.empty? }
24
+ end.select {|s| !s.empty? }.join('\/')
25
25
 
26
- %r(\/#{groups.join('\/')})
26
+ %r(\/#{groups})
27
27
  end
28
28
 
29
29
  def match path
@@ -0,0 +1,44 @@
1
+ module NYNY
2
+ class Router
3
+ NullHandler = Class.new
4
+
5
+ attr_reader :fallback, :routes, :before_hooks, :after_hooks
6
+ def initialize options
7
+ @fallback = options[:fallback]
8
+ @routes = options[:routes]
9
+ @before_hooks = options[:before_hooks]
10
+ @after_hooks = options[:after_hooks]
11
+ end
12
+
13
+ def find_handler request
14
+ routes.fetch(request.request_method.downcase.to_sym, []).each do |sig, h|
15
+ params = sig.match request.path
16
+ return [h, params] if params
17
+ end
18
+
19
+ [NullHandler, {}]
20
+ end
21
+
22
+ def call env
23
+ req = Request.new(env)
24
+ handler, params = find_handler req
25
+
26
+ if handler != NullHandler
27
+ process req, handler, params
28
+ else
29
+ fallback.call env
30
+ end
31
+ end
32
+
33
+ def process request, handler, url_params
34
+ request.params.merge! url_params
35
+ request.params.default_proc = proc {|h,k| h[k.to_s] || h[k.to_sym]}
36
+
37
+ scope = RequestScope.new(request)
38
+ before_hooks.each {|h| scope.instance_eval &h }
39
+ response = scope.apply_to &handler
40
+ after_hooks.each {|h| scope.instance_eval &h }
41
+ response
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,18 @@
1
+ module NYNY
2
+ module Runner
3
+ def optimal_runner
4
+ return Rack::Handler::WEBrick if RUBY_PLATFORM == 'java'
5
+
6
+ begin
7
+ Rack::Handler::Thin
8
+ rescue LoadError
9
+ Rack::Handler::WEBrick
10
+ end
11
+ end
12
+
13
+ def run! port=9292
14
+ middlewares.unshift Rack::ShowExceptions, Rack::CommonLogger
15
+ optimal_runner.run new, :Port => port
16
+ end
17
+ end
18
+ end
@@ -1,3 +1,3 @@
1
1
  module NYNY
2
- VERSION = "1.0.0.pre1"
2
+ VERSION = "1.0.0"
3
3
  end
@@ -8,8 +8,8 @@ Gem::Specification.new do |spec|
8
8
  spec.version = NYNY::VERSION
9
9
  spec.authors = ["Andrei Lisnic"]
10
10
  spec.email = ["andrei.lisnic@gmail.com"]
11
- spec.description = %q{New York, New York.}
12
- spec.summary = %q{sinatra's little brother}
11
+ spec.description = %q{New York, New York - (very) small Sinatra clone.}
12
+ spec.summary = %q{New York, New York.}
13
13
  spec.homepage = "https://github.com/alisnic/nyny"
14
14
  spec.license = "MIT"
15
15
 
@@ -1,16 +1,8 @@
1
- require_relative 'spec_helper'
1
+ require 'spec_helper'
2
2
 
3
3
  describe App do
4
4
  let (:app) { mock_app {} }
5
5
 
6
- describe '.run!' do
7
- #
8
- end
9
-
10
- it 'should have the class methods included' do
11
- extended_modules_for(App).should include(ClassLevelApi)
12
- end
13
-
14
6
  it 'should return a rack response on call' do
15
7
  response = app.get '/'
16
8
  response.should be_a(Rack::Response)
@@ -21,9 +13,17 @@ describe App do
21
13
  response.status.should == 404
22
14
  end
23
15
 
16
+ it '.use_protection! should add protection middleware on top' do
17
+ app_class = mock_app_class do
18
+ use_protection!
19
+ end
20
+
21
+ app_class.middlewares.first.should == [Rack::Protection, {}]
22
+ end
23
+
24
24
  it 'should match a route for any supported verbs' do
25
25
  url = random_url
26
- verb = ClassLevelApi::HTTP_VERBS.sample
26
+ verb = App::HTTP_VERBS.sample
27
27
 
28
28
  app = mock_app do
29
29
  send verb, url do
@@ -82,7 +82,7 @@ describe App do
82
82
  [210, {}, ['Hello from downstream']]
83
83
  end
84
84
 
85
- app_class = frankie_app do
85
+ app_class = mock_app_class do
86
86
  get '/' do
87
87
  'hello'
88
88
  end
@@ -122,23 +122,33 @@ describe App do
122
122
  res.headers['Set-Cookie'].should == 'foo=bar'
123
123
  end
124
124
 
125
- describe '.run!' do
126
- before do
127
- handler = begin
128
- Rack::Handler::Thin
129
- rescue LoadError
130
- Rack::Handler::WEBrick
131
- end
132
- handler.stub :run
125
+ describe 'Class level api' do
126
+ let (:app_class) { Class.new(App) }
127
+ describe 'middlewares' do
128
+ let (:app_class) do
129
+ mock_app_class do
130
+ use NullMiddleware
131
+ end
132
+ end
133
+
134
+ it 'should allow to add a middleware' do
135
+ app_class.middlewares.last.first.should == NullMiddleware
136
+ end
133
137
  end
134
138
 
135
- it 'should include the default middleware on top' do
136
- kls = frankie_app do
139
+ describe 'helpers' do
140
+ it 'should allow to include a helper in request scope' do
141
+ app_class.helpers NullHelper
142
+ RequestScope.ancestors.should include(NullHelper)
137
143
  end
138
144
 
139
- kls.run!
140
- kls.middlewares.first.should == Rack::ShowExceptions
141
- kls.middlewares[1].should == Rack::CommonLogger
145
+ it 'should allow to include multiple helpers modules' do
146
+ module NullHelper2
147
+ end
148
+
149
+ app_class.helpers NullHelper, NullHelper2
150
+ RequestScope.ancestors.should include(NullHelper, NullHelper2)
151
+ end
142
152
  end
143
153
  end
144
154
  end