deas 0.31.0 → 0.32.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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 2e01bef9b6af4303f73139d7163cffe10e365422
4
+ data.tar.gz: 72a8a94d809478d18b0977d6f08db810a4801547
5
+ SHA512:
6
+ metadata.gz: d911b586a2a5550d68b1213da0928c9660bded2411454543f838d5aa882652412d7bc3717c526fd9b53349c5250ce37c66c0feffb5105e8c65ea7382bb0ebf9a
7
+ data.tar.gz: 29061f4db02882144065819d5f86c720cf9859eb100972f88c070ce23c90257b65bf8f52a0cc6c973f3c94b2a6c7746656aa20613916d2be4a2049526ce321da
data/Gemfile CHANGED
@@ -2,6 +2,6 @@ source "https://rubygems.org"
2
2
 
3
3
  gemspec
4
4
 
5
- gem 'rake'
5
+ gem 'rake', "~> 10.4.0"
6
6
  gem 'pry', "~> 0.9.0"
7
7
  gem 'rack-test'
@@ -16,4 +16,6 @@ module Deas
16
16
  end
17
17
  end
18
18
 
19
+ HandlerProxyNotFound = Class.new(Error)
20
+
19
21
  end
@@ -0,0 +1,41 @@
1
+ require 'deas/exceptions'
2
+ require 'deas/sinatra_runner'
3
+
4
+ module Deas
5
+
6
+ class HandlerProxy
7
+
8
+ attr_reader :handler_class_name, :handler_class
9
+
10
+ def initialize(handler_class_name)
11
+ @handler_class_name = handler_class_name
12
+ end
13
+
14
+ def validate!
15
+ raise NotImplementedError
16
+ end
17
+
18
+ def run(sinatra_call)
19
+ runner = SinatraRunner.new(self.handler_class, {
20
+ :sinatra_call => sinatra_call,
21
+ :request => sinatra_call.request,
22
+ :response => sinatra_call.response,
23
+ :session => sinatra_call.session,
24
+ :params => sinatra_call.params,
25
+ :logger => sinatra_call.settings.logger,
26
+ :router => sinatra_call.settings.router,
27
+ :template_source => sinatra_call.settings.template_source
28
+ })
29
+
30
+ sinatra_call.request.env.tap do |env|
31
+ env['deas.params'] = runner.params
32
+ env['deas.handler_class_name'] = self.handler_class.name
33
+ env['deas.logging'].call " Handler: #{env['deas.handler_class_name']}"
34
+ env['deas.logging'].call " Params: #{env['deas.params'].inspect}"
35
+ end
36
+ runner.run
37
+ end
38
+
39
+ end
40
+
41
+ end
@@ -1,9 +1,10 @@
1
+ require 'deas/handler_proxy'
1
2
  require 'deas/url'
2
3
  require 'deas/view_handler'
3
4
 
4
5
  module Deas
5
6
 
6
- class RedirectProxy
7
+ class RedirectProxy < HandlerProxy
7
8
 
8
9
  attr_reader :handler_class_name, :handler_class
9
10
 
data/lib/deas/route.rb CHANGED
@@ -1,39 +1,29 @@
1
- require 'deas/sinatra_runner'
1
+ require 'deas/exceptions'
2
2
 
3
3
  module Deas
4
4
 
5
5
  class Route
6
6
 
7
- attr_reader :method, :path, :route_proxy, :handler_class
7
+ attr_reader :method, :path, :handler_proxies
8
8
 
9
- def initialize(method, path, route_proxy)
10
- @method, @path, @route_proxy = method, path, route_proxy
9
+ def initialize(method, path, handler_proxies)
10
+ @method, @path, @handler_proxies = method, path, handler_proxies
11
11
  end
12
12
 
13
13
  def validate!
14
- @route_proxy.validate!
15
- @handler_class = @route_proxy.handler_class
14
+ @handler_proxies.each do |request_type_name, proxy|
15
+ proxy.validate!
16
+ end
16
17
  end
17
18
 
18
19
  def run(sinatra_call)
19
- runner = SinatraRunner.new(self.handler_class, {
20
- :sinatra_call => sinatra_call,
21
- :request => sinatra_call.request,
22
- :response => sinatra_call.response,
23
- :session => sinatra_call.session,
24
- :params => sinatra_call.params,
25
- :logger => sinatra_call.settings.logger,
26
- :router => sinatra_call.settings.router,
27
- :template_source => sinatra_call.settings.template_source
28
- })
29
-
30
- sinatra_call.request.env.tap do |env|
31
- env['deas.params'] = runner.params
32
- env['deas.handler_class_name'] = self.handler_class.name
33
- env['deas.logging'].call " Handler: #{env['deas.handler_class_name']}"
34
- env['deas.logging'].call " Params: #{env['deas.params'].inspect}"
20
+ type = sinatra_call.settings.router.request_type_name(sinatra_call.request)
21
+ proxy = begin
22
+ @handler_proxies[type]
23
+ rescue HandlerProxyNotFound
24
+ sinatra_call.halt(404)
35
25
  end
36
- runner.run
26
+ proxy.run(sinatra_call)
37
27
  end
38
28
 
39
29
  end
@@ -1,18 +1,22 @@
1
1
  require 'deas/exceptions'
2
- require 'deas/view_handler'
2
+ require 'deas/handler_proxy'
3
3
 
4
4
  module Deas
5
- class RouteProxy
6
5
 
7
- attr_reader :handler_class_name, :handler_class
6
+ class RouteProxy < HandlerProxy
8
7
 
9
- def initialize(handler_class_name)
10
- @handler_class_name = handler_class_name
8
+ def initialize(handler_class_name, view_handler_ns = nil)
9
+ raise(NoHandlerClassError.new(handler_class_name)) if handler_class_name.nil?
10
+
11
+ if view_handler_ns && !(handler_class_name =~ /^::/)
12
+ handler_class_name = "#{view_handler_ns}::#{handler_class_name}"
13
+ end
14
+ super(handler_class_name)
11
15
  end
12
16
 
13
17
  def validate!
14
- @handler_class = constantize(@handler_class_name).tap do |handler_class|
15
- raise(NoHandlerClassError.new(@handler_class_name)) if !handler_class
18
+ @handler_class = constantize(self.handler_class_name).tap do |handler_class|
19
+ raise(NoHandlerClassError.new(self.handler_class_name)) if !handler_class
16
20
  end
17
21
  end
18
22
 
@@ -27,4 +31,5 @@ module Deas
27
31
  end
28
32
 
29
33
  end
34
+
30
35
  end
data/lib/deas/router.rb CHANGED
@@ -1,15 +1,21 @@
1
+ require 'deas/exceptions'
1
2
  require 'deas/redirect_proxy'
2
3
  require 'deas/route'
3
4
  require 'deas/route_proxy'
4
5
  require 'deas/url'
5
6
 
6
7
  module Deas
8
+
7
9
  class Router
8
10
 
9
- attr_accessor :urls, :routes
11
+ DEFAULT_REQUEST_TYPE_NAME = 'default'
12
+
13
+ attr_reader :request_types, :urls, :routes
10
14
 
11
15
  def initialize(&block)
16
+ @request_types = []
12
17
  @urls, @routes = {}, []
18
+ default_request_type_name(DEFAULT_REQUEST_TYPE_NAME)
13
19
  self.instance_eval(&block) if !block.nil?
14
20
  end
15
21
 
@@ -41,21 +47,42 @@ module Deas
41
47
  prepend_base_url(url.path_for(*args))
42
48
  end
43
49
 
44
- def get(path, handler_name); self.route(:get, path, handler_name); end
45
- def post(path, handler_name); self.route(:post, path, handler_name); end
46
- def put(path, handler_name); self.route(:put, path, handler_name); end
47
- def patch(path, handler_name); self.route(:patch, path, handler_name); end
48
- def delete(path, handler_name); self.route(:delete, path, handler_name); end
50
+ def default_request_type_name(value = nil)
51
+ @default_request_type = RequestType.new(value) if !value.nil?
52
+ @default_request_type.name
53
+ end
54
+
55
+ def add_request_type(name, &proc)
56
+ @request_types << RequestType.new(name, proc)
57
+ end
58
+
59
+ # ideally someday the request should just *know* its request type
60
+ def request_type_name(request)
61
+ (self.request_types.detect{ |rt| rt.proc.call(request) } || @default_request_type).name
62
+ end
49
63
 
50
- def route(http_method, from_path, handler_class_name)
51
- if self.view_handler_ns && !(handler_class_name =~ /^::/)
52
- handler_class_name = "#{self.view_handler_ns}::#{handler_class_name}"
64
+ def get(path, *args); self.route(:get, path, *args); end
65
+ def post(path, *args); self.route(:post, path, *args); end
66
+ def put(path, *args); self.route(:put, path, *args); end
67
+ def patch(path, *args); self.route(:patch, path, *args); end
68
+ def delete(path, *args); self.route(:delete, path, *args); end
69
+
70
+ def route(http_method, from_path, *args)
71
+ handler_names = args.last.kind_of?(::Hash) ? args.pop : {}
72
+ default_handler_name = args.last
73
+ if !handler_names.key?(self.default_request_type_name) && default_handler_name
74
+ handler_names[self.default_request_type_name] = default_handler_name
75
+ end
76
+
77
+ proxies = handler_names.inject({}) do |proxies, (req_type_name, handler_name)|
78
+ proxies[req_type_name] = Deas::RouteProxy.new(handler_name, self.view_handler_ns)
79
+ proxies
53
80
  end
54
- proxy = Deas::RouteProxy.new(handler_class_name)
55
81
 
56
82
  from_url = self.urls[from_path]
57
83
  from_url_path = from_url.path if from_url
58
- add_route(http_method, prepend_base_url(from_url_path || from_path), proxy)
84
+
85
+ add_route(http_method, prepend_base_url(from_url_path || from_path), proxies)
59
86
  end
60
87
 
61
88
  def redirect(from_path, to_path = nil, &block)
@@ -63,11 +90,14 @@ module Deas
63
90
  if to_path.kind_of?(::Symbol) && to_url.nil?
64
91
  raise ArgumentError, "no url named `#{to_path.inspect}`"
65
92
  end
93
+
66
94
  proxy = Deas::RedirectProxy.new(to_url || to_path, &block)
95
+ proxies = { self.default_request_type_name => proxy }
67
96
 
68
97
  from_url = self.urls[from_path]
69
98
  from_url_path = from_url.path if from_url
70
- add_route(:get, from_url_path || from_path, proxy)
99
+
100
+ add_route(:get, prepend_base_url(from_url_path || from_path), proxies)
71
101
  end
72
102
 
73
103
  private
@@ -76,10 +106,36 @@ module Deas
76
106
  self.urls[name] = Deas::Url.new(name, path)
77
107
  end
78
108
 
79
- def add_route(http_method, path, proxy)
80
- Deas::Route.new(http_method, path, proxy).tap{ |r| self.routes.push(r) }
109
+ def add_route(http_method, path, proxies)
110
+ proxies = HandlerProxies.new(proxies, self.default_request_type_name)
111
+ Deas::Route.new(http_method, path, proxies).tap{ |r| self.routes.push(r) }
81
112
  end
82
113
 
114
+ class HandlerProxies
115
+
116
+ attr_reader :default_type
117
+
118
+ def initialize(proxies, default_name)
119
+ @proxies = proxies
120
+ @default_type = default_name
121
+ end
122
+
123
+ def [](type)
124
+ @proxies[type] || @proxies[@default_type] || raise(HandlerProxyNotFound)
125
+ end
126
+
127
+ def each(&block)
128
+ @proxies.each(&block)
129
+ end
130
+
131
+ def empty?
132
+ @proxies.empty?
133
+ end
134
+
135
+ end
136
+
137
+ RequestType = Struct.new(:name, :proc)
138
+
83
139
  end
84
140
 
85
141
  end
data/lib/deas/server.rb CHANGED
@@ -211,6 +211,10 @@ module Deas::Server
211
211
  def url(*args, &block); self.router.url(*args, &block); end
212
212
  def url_for(*args, &block); self.router.url_for(*args, &block); end
213
213
 
214
+ def default_request_type_name(*args); self.router.default_request_type_name(*args); end
215
+ def add_request_type(*args, &block); self.router.add_request_type(*args, &block); end
216
+ def request_type_name(*args); self.router.request_type_name(*args); end
217
+
214
218
  def get(*args, &block); self.router.get(*args, &block); end
215
219
  def post(*args, &block); self.router.post(*args, &block); end
216
220
  def put(*args, &block); self.router.put(*args, &block); end
data/lib/deas/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Deas
2
- VERSION = "0.31.0"
2
+ VERSION = "0.32.0"
3
3
  end
@@ -19,6 +19,10 @@ class DeasTestServer
19
19
  end
20
20
  end
21
21
 
22
+ default_request_type_name 'desktop'
23
+ add_request_type('regular'){ |r| r.path_info =~ /regular/ }
24
+ add_request_type('mobile'){ |r| r.path_info =~ /mobile/ }
25
+
22
26
  get '/show', 'ShowHandler'
23
27
  get '/show.html', 'ShowHtmlHandler'
24
28
  get '/show.json', 'ShowJsonHandler'
@@ -26,6 +30,9 @@ class DeasTestServer
26
30
  get '/show-text', 'ShowTextHandler'
27
31
  get '/show-headers-text', 'ShowHeadersTextHandler'
28
32
 
33
+ get '/req-type-show/:type', 'regular' => 'ShowHandler',
34
+ 'mobile' => 'ShowMobileHandler'
35
+
29
36
  get '/halt', 'HaltHandler'
30
37
  get '/error', 'ErrorHandler'
31
38
  get '/redirect', 'RedirectHandler'
@@ -71,6 +78,21 @@ class ShowHandler
71
78
 
72
79
  end
73
80
 
81
+ class ShowMobileHandler
82
+ include Deas::ViewHandler
83
+
84
+ attr_reader :message
85
+
86
+ def init!
87
+ @message = "[MOBILE] #{params['message']}"
88
+ end
89
+
90
+ def run!
91
+ @message
92
+ end
93
+
94
+ end
95
+
74
96
  class ShowHtmlHandler
75
97
  include Deas::ViewHandler
76
98
 
@@ -1,7 +1,8 @@
1
1
  require 'assert'
2
- require 'assert-rack-test'
3
2
  require 'deas'
4
3
 
4
+ require 'assert-rack-test'
5
+
5
6
  module Deas
6
7
 
7
8
  class RackTestsContext < Assert::Context
@@ -20,8 +21,7 @@ module Deas
20
21
  get '/show', 'message' => 'this is a test'
21
22
 
22
23
  assert_equal 200, last_response.status
23
- exp = "this is a test"
24
- assert_equal exp, last_response.body
24
+ assert_equal "this is a test", last_response.body
25
25
  end
26
26
 
27
27
  should "set the content type appropriately" do
@@ -44,6 +44,19 @@ module Deas
44
44
  assert_equal 'text/plain', last_response.headers['Content-Type']
45
45
  end
46
46
 
47
+ should "render different handlers for the same meth/path based on the type" do
48
+ get '/req-type-show/regular', 'message' => 'this is a test request'
49
+ assert_equal 200, last_response.status
50
+ assert_equal "this is a test request", last_response.body
51
+
52
+ get '/req-type-show/mobile', 'message' => 'this is a test request'
53
+ assert_equal 200, last_response.status
54
+ assert_equal "[MOBILE] this is a test request", last_response.body
55
+
56
+ get '/req-type-show/other', 'message' => 'this is a test request'
57
+ assert_equal 404, last_response.status
58
+ end
59
+
47
60
  should "allow halting with a custom response" do
48
61
  get '/halt', 'with' => 234
49
62
 
@@ -115,7 +128,6 @@ module Deas
115
128
  desc "handler"
116
129
  setup do
117
130
  get 'handler/tests?a-param=something'
118
-
119
131
  @data_inspect = last_response.body
120
132
  end
121
133
 
@@ -11,7 +11,7 @@ module Deas
11
11
  assert_kind_of RuntimeError, Deas::Error.new
12
12
  end
13
13
 
14
- should "provide a no handler class exception that subclasses `Error`" do
14
+ should "provide a no handler class exception" do
15
15
  assert Deas::NoHandlerClassError
16
16
 
17
17
  handler_class_name = 'AHandlerClass'
@@ -23,12 +23,12 @@ module Deas
23
23
  assert_equal exp_msg, e.message
24
24
  end
25
25
 
26
- should "provide a server exception that subclasses `Error`" do
26
+ should "provide a server exception" do
27
27
  assert Deas::ServerError
28
28
  assert_kind_of Deas::Error, Deas::ServerError.new
29
29
  end
30
30
 
31
- should "provide a server root exception that subclasses `ServerError`" do
31
+ should "provide a server root exception" do
32
32
  assert Deas::ServerRootError
33
33
 
34
34
  e = Deas::ServerRootError.new
@@ -36,6 +36,11 @@ module Deas
36
36
  assert_equal "server `root` not set but required", e.message
37
37
  end
38
38
 
39
+ should "provide a handler proxy not found exception" do
40
+ assert Deas::HandlerProxyNotFound
41
+ assert_kind_of Deas::Error, Deas::HandlerProxyNotFound.new
42
+ end
43
+
39
44
  end
40
45
 
41
46
  end
@@ -0,0 +1,115 @@
1
+ require 'assert'
2
+ require 'deas/handler_proxy'
3
+
4
+ require 'deas/exceptions'
5
+ require 'deas/sinatra_runner'
6
+ require 'test/support/fake_sinatra_call'
7
+ require 'test/support/view_handlers'
8
+
9
+ class Deas::HandlerProxy
10
+
11
+ class UnitTests < Assert::Context
12
+ desc "Deas::HandlerProxy"
13
+ setup do
14
+ @proxy = Deas::HandlerProxy.new('EmptyViewHandler')
15
+ end
16
+ subject{ @proxy }
17
+
18
+ should have_readers :handler_class_name, :handler_class
19
+ should have_imeths :validate!, :run
20
+
21
+ should "know its handler class name" do
22
+ assert_equal 'EmptyViewHandler', subject.handler_class_name
23
+ end
24
+
25
+ should "not implement its validate! method" do
26
+ assert_raises(NotImplementedError){ subject.validate! }
27
+ end
28
+
29
+ end
30
+
31
+ class RunTests < UnitTests
32
+ desc "when run"
33
+ setup do
34
+ @runner_spy = SinatraRunnerSpy.new
35
+ Assert.stub(Deas::SinatraRunner, :new) do |*args|
36
+ @runner_spy.build(*args)
37
+ @runner_spy
38
+ end
39
+
40
+ Assert.stub(@proxy, :handler_class){ EmptyViewHandler }
41
+
42
+ @fake_sinatra_call = FakeSinatraCall.new
43
+ @proxy.run(@fake_sinatra_call)
44
+ end
45
+
46
+ should "build and run a sinatra runner" do
47
+ assert_equal subject.handler_class, @runner_spy.handler_class
48
+
49
+ exp_args = {
50
+ :sinatra_call => @fake_sinatra_call,
51
+ :request => @fake_sinatra_call.request,
52
+ :response => @fake_sinatra_call.response,
53
+ :session => @fake_sinatra_call.session,
54
+ :params => @fake_sinatra_call.params,
55
+ :logger => @fake_sinatra_call.settings.logger,
56
+ :router => @fake_sinatra_call.settings.router,
57
+ :template_source => @fake_sinatra_call.settings.template_source
58
+ }
59
+ assert_equal exp_args, @runner_spy.args
60
+
61
+ assert_true @runner_spy.run_called
62
+ end
63
+
64
+ should "add the runner params to the request env" do
65
+ exp = @runner_spy.params
66
+ assert_equal exp, @fake_sinatra_call.request.env['deas.params']
67
+ end
68
+
69
+ should "add the handler class name to the request env" do
70
+ exp = subject.handler_class.name
71
+ assert_equal exp, @fake_sinatra_call.request.env['deas.handler_class_name']
72
+ end
73
+
74
+ should "log the handler and params" do
75
+ exp_msgs = [
76
+ " Handler: #{subject.handler_class}",
77
+ " Params: #{@runner_spy.params.inspect}"
78
+ ]
79
+ assert_equal exp_msgs, @fake_sinatra_call.request.logging_msgs
80
+ end
81
+
82
+ end
83
+
84
+ class SinatraRunnerSpy
85
+
86
+ attr_reader :run_called
87
+ attr_reader :handler_class, :args
88
+ attr_reader :sinatra_call
89
+ attr_reader :request, :response, :session, :params
90
+ attr_reader :logger, :router, :template_source
91
+
92
+ def initialize
93
+ @run_called = false
94
+ end
95
+
96
+ def build(handler_class, args)
97
+ @handler_class, @args = handler_class, args
98
+
99
+ @sinatra_call = args[:sinatra_call]
100
+ @request = args[:request]
101
+ @response = args[:response]
102
+ @session = args[:session]
103
+ @params = args[:params]
104
+ @logger = args[:logger]
105
+ @router = args[:router]
106
+ @template_source = args[:template_source]
107
+ end
108
+
109
+ def run
110
+ @run_called = true
111
+ end
112
+
113
+ end
114
+
115
+ end