nyny 1.0.0.pre1 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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