rasti-web 1.1.0 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 8be22c6b9730ecebfc6deb7449fc33fc580eedfa
4
- data.tar.gz: 260d7b82a6a975f7872c0e6d5a2cff49f526272f
2
+ SHA256:
3
+ metadata.gz: 250a8b04a93b51665f10b49206453b490ffc83c81b8684ca466d4259ea23e75a
4
+ data.tar.gz: 9c32ed8ab0506cb4a4b407bb50fa6f12dc19bafc457e05181f18ba16ad9c5032
5
5
  SHA512:
6
- metadata.gz: 2255e469c1232c3f66a83e17aea842e34f55d51e7c23656be60f30b90785c864e08aed79dd6f6a6f7ebc72256ab95bfc218c6a077418ec0c09c3821e16fdc119
7
- data.tar.gz: ee292a32f9ec7142d1bb0ab386d635fb128bcc277e44bda50430761b183d361d699bdce02e531766bf5513a4ae08011081ddda445c1258146387e95af144a4c8
6
+ metadata.gz: f30ddbfed0f369d8eb9bcabe5e43b2a9891b9a53d3be55bbe3b4d492cf1724984e25b6a1ca91285879bedc14b8ec691525a345bf62dff48cc72f2d47eab80a8f
7
+ data.tar.gz: 120e466b7433332064adcc6ebfaf8cfc337433221954fddeb867a91cc801e53e8856a1b00a331c6867a1845d3c248f9febe1ac24a4e1864d3cd08ec511d3326d
@@ -1 +1 @@
1
- ruby-2.3.0
1
+ ruby-2.5.7
@@ -1,11 +1,20 @@
1
1
  language: ruby
2
+
2
3
  rvm:
3
- - 1.9.3
4
4
  - 2.0
5
5
  - 2.1
6
6
  - 2.2
7
- - 2.3.0
8
- - 2.4.0
9
- - jruby
10
- before_install:
11
- - gem install bundler
7
+ - 2.3
8
+ - 2.4
9
+ - 2.5
10
+ - 2.6
11
+ - 2.7
12
+ - jruby-9.2.9.0
13
+ - ruby-head
14
+ - jruby-head
15
+
16
+ matrix:
17
+ fast_finish: true
18
+ allow_failures:
19
+ - rvm: ruby-head
20
+ - rvm: jruby-head
data/README.md CHANGED
@@ -4,7 +4,6 @@
4
4
  [![Build Status](https://travis-ci.org/gabynaiman/rasti-web.svg?branch=master)](https://travis-ci.org/gabynaiman/rasti-web)
5
5
  [![Coverage Status](https://coveralls.io/repos/gabynaiman/rasti-web/badge.svg?branch=master)](https://coveralls.io/r/gabynaiman/rasti-web?branch=master)
6
6
  [![Code Climate](https://codeclimate.com/github/gabynaiman/rasti-web.svg)](https://codeclimate.com/github/gabynaiman/rasti-web)
7
- [![Dependency Status](https://gemnasium.com/gabynaiman/rasti-web.svg)](https://gemnasium.com/gabynaiman/rasti-web)
8
7
 
9
8
  Web blocks to build robust applications
10
9
 
@@ -62,6 +61,38 @@ class UsersController < Rasti::Web::Controller
62
61
  end
63
62
  ```
64
63
 
64
+ ### Hooks
65
+
66
+ ```ruby
67
+ class UsersController < Rasti::Web::Controller
68
+
69
+ before_action do |action_name|
70
+ end
71
+
72
+ before_action :action_name do
73
+ end
74
+
75
+ after_action do |action_name|
76
+ end
77
+
78
+ after_action :action_name do
79
+ end
80
+
81
+ end
82
+ ```
83
+
84
+ ### Error handling
85
+
86
+ ```ruby
87
+ class UsersController < Rasti::Web::Controller
88
+
89
+ rescue_from StandardError do |ex|
90
+ render.status 500, 'Unexpected error'
91
+ end
92
+
93
+ end
94
+ ```
95
+
65
96
  ## Contributing
66
97
 
67
98
  1. Fork it ( https://github.com/gabynaiman/rasti-web/fork )
data/Rakefile CHANGED
@@ -3,7 +3,8 @@ require 'rake/testtask'
3
3
 
4
4
  Rake::TestTask.new(:spec) do |t|
5
5
  t.libs << 'spec'
6
- t.pattern = 'spec/**/*_spec.rb'
6
+ t.libs << 'lib'
7
+ t.pattern = ENV['DIR'] ? File.join(ENV['DIR'], '**', '*_spec.rb') : 'spec/**/*_spec.rb'
7
8
  t.verbose = false
8
9
  t.warning = false
9
10
  t.loader = nil if ENV['TEST']
@@ -16,6 +16,7 @@ require_relative 'web/application'
16
16
  require_relative 'web/template'
17
17
  require_relative 'web/view_context'
18
18
  require_relative 'web/render'
19
+ require_relative 'web/headers'
19
20
  require_relative 'web/request'
20
21
  require_relative 'web/controller'
21
22
  require_relative 'web/version'
@@ -1,7 +1,7 @@
1
1
  module Rasti
2
2
  module Web
3
3
  class Controller
4
-
4
+
5
5
  extend Forwardable
6
6
 
7
7
  def_delegators :request, :params, :session
@@ -16,37 +16,73 @@ module Rasti
16
16
  end
17
17
 
18
18
  class << self
19
+
19
20
  def action(action_name)
20
21
  raise "Undefined action '#{action_name}' in #{name}" unless instance_methods.include? action_name.to_sym
21
-
22
+
22
23
  Endpoint.new do |req, res, render|
23
24
  controller = new req, res, render
24
25
  begin
26
+ call_before_action controller, action_name
25
27
  controller.public_send action_name
26
28
  rescue => ex
27
- exception_class = handled_exceptions.detect { |klass| ex.is_a? klass }
28
- if exception_class
29
- controller.instance_exec ex, &exception_handlers[exception_class]
30
- else
31
- raise ex
32
- end
29
+ call_exception_handler controller, ex
30
+ ensure
31
+ call_after_action controller, action_name
33
32
  end
34
33
  end
35
34
  end
36
35
 
37
36
  alias_method :>>, :action
38
37
 
39
- def exception_handlers
40
- @exception_handlers ||= superclass.respond_to?(:exception_handlers) ? superclass.exception_handlers : {}
38
+ def before_action(action_name=nil, &block)
39
+ before_action_hooks[action_name] = block
41
40
  end
42
41
 
43
- def handled_exceptions
44
- @handled_exceptions ||= ClassAncestrySort.desc exception_handlers.keys
42
+ def after_action(action_name=nil, &block)
43
+ after_action_hooks[action_name] = block
45
44
  end
46
45
 
47
46
  def rescue_from(exception_class, &block)
48
47
  exception_handlers[exception_class] = block
49
48
  end
49
+
50
+ def call_before_action(controller, action_name)
51
+ hook = before_action_hooks[action_name] || before_action_hooks[nil]
52
+ controller.instance_exec action_name, &hook if hook
53
+ end
54
+
55
+ def call_after_action(controller, action_name)
56
+ hook = after_action_hooks[action_name] || after_action_hooks[nil]
57
+ controller.instance_exec action_name, &hook if hook
58
+ end
59
+
60
+ def call_exception_handler(controller, exception)
61
+ exception_class = handled_exceptions.detect { |klass| exception.is_a? klass }
62
+ exception_handler = exception_handlers[exception_class]
63
+ if exception_handler
64
+ controller.instance_exec exception, &exception_handler
65
+ else
66
+ raise exception
67
+ end
68
+ end
69
+
70
+ def before_action_hooks
71
+ @before_action_hooks ||= superclass.respond_to?(:before_action_hooks) ? superclass.before_action_hooks.dup : {}
72
+ end
73
+
74
+ def after_action_hooks
75
+ @after_action_hooks ||= superclass.respond_to?(:after_action_hooks) ? superclass.after_action_hooks.dup : {}
76
+ end
77
+
78
+ def exception_handlers
79
+ @exception_handlers ||= superclass.respond_to?(:exception_handlers) ? superclass.exception_handlers.dup : {}
80
+ end
81
+
82
+ def handled_exceptions
83
+ @handled_exceptions ||= ClassAncestrySort.desc exception_handlers.keys
84
+ end
85
+
50
86
  end
51
87
 
52
88
  end
@@ -1,7 +1,7 @@
1
1
  module Rasti
2
2
  module Web
3
3
  class Endpoint
4
-
4
+
5
5
  def initialize(&block)
6
6
  @block = block
7
7
  end
@@ -0,0 +1,48 @@
1
+ module Rasti
2
+ module Web
3
+ class Headers
4
+
5
+ CONTENT_TYPES = {
6
+ text: 'text/plain',
7
+ html: 'text/html',
8
+ json: 'application/json',
9
+ js: 'application/javascript',
10
+ css: 'text/css'
11
+ }
12
+
13
+ class << self
14
+
15
+ CONTENT_TYPES.each do |key, value|
16
+ define_method "for_#{key}" do |charset:'utf-8'|
17
+ content_type value, charset: charset
18
+ end
19
+ end
20
+
21
+ def for_file(filename, attachment:true, charset:'utf-8')
22
+ merge content_type(MIME::Types.of(filename).first.content_type, charset: charset),
23
+ content_disposition(filename, attachment: attachment)
24
+ end
25
+
26
+ private
27
+
28
+ def content_type(type, charset:'utf-8')
29
+ {'Content-Type' => "#{type}; charset=#{charset}"}
30
+ end
31
+
32
+ def content_disposition(filename, attachment:true)
33
+ args = []
34
+ args << 'attachment' if attachment
35
+ args << "filename=\"#{File.basename(filename)}\""
36
+ {'Content-Disposition' => args.join("; ")}
37
+ end
38
+
39
+ def merge(*poperties)
40
+ poperties.inject({}) do |result, prop|
41
+ result.merge prop
42
+ end
43
+ end
44
+
45
+ end
46
+ end
47
+ end
48
+ end
@@ -11,60 +11,63 @@ module Rasti
11
11
  end
12
12
 
13
13
  def status(status, *args)
14
- respond_with status,
15
- extract_headers(args),
14
+ respond_with status,
15
+ extract_headers(args),
16
16
  extract_body(args)
17
17
  end
18
18
 
19
19
  def text(text, *args)
20
- respond_with extract_status(args),
21
- extract_headers(args).merge('Content-Type' => 'text/plain; charset=utf-8'),
20
+ respond_with extract_status(args),
21
+ extract_headers(args).merge(Headers.for_text),
22
22
  text
23
23
  end
24
24
 
25
25
  def html(html, *args)
26
- respond_with extract_status(args),
27
- extract_headers(args).merge('Content-Type' => 'text/html; charset=utf-8'),
26
+ respond_with extract_status(args),
27
+ extract_headers(args).merge(Headers.for_html),
28
28
  html
29
29
  end
30
30
 
31
31
  def json(object, *args)
32
- respond_with extract_status(args),
33
- extract_headers(args).merge('Content-Type' => 'application/json; charset=utf-8'),
32
+ respond_with extract_status(args),
33
+ extract_headers(args).merge(Headers.for_json),
34
34
  object.is_a?(String) ? object : JSON.dump(object)
35
35
  end
36
36
 
37
37
  def js(script, *args)
38
- respond_with extract_status(args),
39
- extract_headers(args).merge('Content-Type' => 'application/javascript; charset=utf-8'),
38
+ respond_with extract_status(args),
39
+ extract_headers(args).merge(Headers.for_js),
40
40
  script
41
41
  end
42
42
 
43
43
  def css(stylesheet, *args)
44
- respond_with extract_status(args),
45
- extract_headers(args).merge('Content-Type' => 'text/css; charset=utf-8'),
44
+ respond_with extract_status(args),
45
+ extract_headers(args).merge(Headers.for_css),
46
46
  stylesheet
47
47
  end
48
48
 
49
49
  def file(filename, *args)
50
- content_type = MIME::Types.of(filename).first.content_type
51
- body = File.read filename
50
+ respond_with extract_status(args),
51
+ Headers.for_file(filename).merge(extract_headers(args)),
52
+ File.read(filename)
53
+ end
52
54
 
53
- respond_with extract_status(args),
54
- extract_headers(args).merge('Content-Type' => content_type),
55
- body
55
+ def data(content, *args)
56
+ respond_with extract_status(args),
57
+ extract_headers(args),
58
+ content
56
59
  end
57
60
 
58
61
  def partial(template, locals={})
59
- response['Content-Type'] = 'text/html; charset=utf-8'
62
+ response.headers.merge! Headers.for_html
60
63
  response.write view_context.render(template, locals)
61
64
  end
62
65
 
63
66
  def layout(template=nil, &block)
64
67
  content = block.call if block
65
68
  layout = view_context.render(template || Web.default_layout) { content }
66
-
67
- response['Content-Type'] = 'text/html; charset=utf-8'
69
+
70
+ response.headers.merge! Headers.for_html
68
71
  response.write layout
69
72
  end
70
73
 
@@ -81,7 +84,7 @@ module Rasti
81
84
  end
82
85
 
83
86
  def extract_status(args)
84
- args.detect { |a| a.is_a? Fixnum }
87
+ args.detect { |a| a.is_a? Integer }
85
88
  end
86
89
 
87
90
  def extract_headers(args)
@@ -12,7 +12,7 @@ module Rasti
12
12
  end
13
13
 
14
14
  def body_text
15
- @body_text ||= begin
15
+ @body_text ||= begin
16
16
  text = body.read
17
17
  body.rewind
18
18
  text
@@ -20,8 +20,8 @@ module Rasti
20
20
  end
21
21
 
22
22
  def json?
23
- content_type && ContentType.parse(content_type).mime_type == 'application/json'
24
- rescue
23
+ !content_type.nil? && ContentType.parse(content_type).mime_type == 'application/json'
24
+ rescue
25
25
  false
26
26
  end
27
27
 
@@ -17,7 +17,7 @@ module Rasti
17
17
  end
18
18
 
19
19
  def call(env)
20
- route = route_for env
20
+ route = route_for env
21
21
  env[ROUTE_PARAMS] = route.extract_params env['PATH_INFO']
22
22
  route.call env
23
23
  end
@@ -7,7 +7,7 @@ module Rasti
7
7
  template_file = files.detect { |f| File.exists? f }
8
8
 
9
9
  raise "Missing template #{template} [#{files.join(', ')}]" unless template_file
10
-
10
+
11
11
  tilt = cache.fetch(template_file) { Tilt.new template_file }
12
12
  tilt.render(context, locals, &block)
13
13
  end
@@ -17,7 +17,7 @@ module Rasti
17
17
  def self.cache
18
18
  Thread.current[:templates_cache] ||= Tilt::Cache.new
19
19
  end
20
-
20
+
21
21
  end
22
22
  end
23
23
  end
@@ -1,5 +1,5 @@
1
1
  module Rasti
2
2
  module Web
3
- VERSION = '1.1.0'
3
+ VERSION = '2.1.0'
4
4
  end
5
5
  end
@@ -20,26 +20,18 @@ Gem::Specification.new do |spec|
20
20
 
21
21
  spec.add_dependency 'rack', '~> 1.0'
22
22
  spec.add_dependency 'tilt', '~> 2.0'
23
- spec.add_dependency 'mime-types', '~> 2.0'
23
+ spec.add_dependency 'mime-types', '~> 3.0'
24
24
  spec.add_dependency 'class_config', '~> 0.0.2'
25
25
  spec.add_dependency 'hash_ext', '~> 0.1'
26
26
  spec.add_dependency 'content-type', '~> 0.0'
27
27
  spec.add_dependency 'class_ancestry_sort', '~> 0.1'
28
28
 
29
- spec.add_development_dependency 'bundler', '~> 1.12'
30
29
  spec.add_development_dependency 'rake', '~> 11.0'
31
- spec.add_development_dependency 'minitest', '~> 5.0'
30
+ spec.add_development_dependency 'minitest', '~> 5.0', '< 5.11'
32
31
  spec.add_development_dependency 'minitest-colorin', '~> 0.1'
33
32
  spec.add_development_dependency 'minitest-line', '~> 0.6'
34
33
  spec.add_development_dependency 'simplecov', '~> 0.12'
35
34
  spec.add_development_dependency 'coveralls', '~> 0.8'
36
35
  spec.add_development_dependency 'pry-nav', '~> 0.2'
37
36
  spec.add_development_dependency 'rack-test', '~> 0.6'
38
-
39
- if RUBY_VERSION < '2'
40
- spec.add_development_dependency 'term-ansicolor', '~> 1.3.0'
41
- spec.add_development_dependency 'tins', '~> 1.6.0'
42
- spec.add_development_dependency 'json', '~> 1.8'
43
- spec.add_development_dependency 'parslet', '~> 1.7.0'
44
- end
45
37
  end
@@ -8,7 +8,7 @@ class TestMiddleware
8
8
  def call(env)
9
9
  if env['PATH_INFO'] == '/private'
10
10
  [403, {}, ['Permission denied']]
11
- else
11
+ else
12
12
  @app.call env
13
13
  end
14
14
  end
@@ -50,7 +50,7 @@ describe Rasti::Web::Application do
50
50
 
51
51
  it 'Defined route' do
52
52
  get '/'
53
-
53
+
54
54
  last_response.status.must_equal 200
55
55
  last_response['Content-Type'].must_equal 'text/html; charset=utf-8'
56
56
  last_response.body.must_equal 'Page content'
@@ -58,7 +58,7 @@ describe Rasti::Web::Application do
58
58
 
59
59
  it 'Not found' do
60
60
  get '/not_found'
61
-
61
+
62
62
  last_response.status.must_equal 404
63
63
  last_response.body.must_equal 'Page not found'
64
64
  end
@@ -77,5 +77,5 @@ describe Rasti::Web::Application do
77
77
  last_response['Content-Type'].must_equal 'application/json; charset=utf-8'
78
78
  last_response.body.must_equal '{"id":123}'
79
79
  end
80
-
80
+
81
81
  end
@@ -1,8 +1,29 @@
1
1
  require 'minitest_helper'
2
2
 
3
3
  class TestController < Rasti::Web::Controller
4
+
4
5
  CustomError = Class.new StandardError
5
-
6
+
7
+ def self.hooks_log
8
+ @hooks_log ||= []
9
+ end
10
+
11
+ before_action do |action_name|
12
+ self.class.hooks_log << "Before all: #{action_name}"
13
+ end
14
+
15
+ after_action do |action_name|
16
+ self.class.hooks_log << "After all: #{action_name}"
17
+ end
18
+
19
+ before_action :test do
20
+ self.class.hooks_log << 'Before single: test'
21
+ end
22
+
23
+ after_action :test do
24
+ self.class.hooks_log << 'After single: test'
25
+ end
26
+
6
27
  def test
7
28
  render.html 'Test HTML'
8
29
  end
@@ -30,7 +51,11 @@ class TestController < Rasti::Web::Controller
30
51
  end
31
52
 
32
53
  describe Rasti::Web::Controller do
33
-
54
+
55
+ before do
56
+ TestController.hooks_log.clear
57
+ end
58
+
34
59
  it 'Action endpoint' do
35
60
  action = TestController.action :test
36
61
  env = Rack::MockRequest.env_for '/test'
@@ -40,11 +65,17 @@ describe Rasti::Web::Controller do
40
65
  status.must_equal 200
41
66
  headers['Content-Type'].must_equal 'text/html; charset=utf-8'
42
67
  response.body.must_equal ['Test HTML']
68
+
69
+ TestController.hooks_log.must_equal [
70
+ 'Before single: test',
71
+ 'After single: test'
72
+ ]
43
73
  end
44
74
 
45
75
  it 'Invalid action' do
46
76
  error = proc { TestController.action :invalid }.must_raise RuntimeError
47
77
  error.message.must_equal "Undefined action 'invalid' in TestController"
78
+ TestController.hooks_log.must_be_empty
48
79
  end
49
80
 
50
81
  it 'Rescue explicit exception' do
@@ -53,7 +84,13 @@ describe Rasti::Web::Controller do
53
84
  status, headers, response = action.call env
54
85
 
55
86
  status.must_equal 500
87
+ headers['Content-Type'].must_be_nil
56
88
  response.body.must_equal ['Explicit error']
89
+
90
+ TestController.hooks_log.must_equal [
91
+ 'Before all: explicit_fail',
92
+ 'After all: explicit_fail'
93
+ ]
57
94
  end
58
95
 
59
96
  it 'Rescue implicit exception' do
@@ -62,15 +99,26 @@ describe Rasti::Web::Controller do
62
99
  status, headers, response = action.call env
63
100
 
64
101
  status.must_equal 500
102
+ headers['Content-Type'].must_be_nil
65
103
  response.body.must_equal ['Implicit error']
104
+
105
+ TestController.hooks_log.must_equal [
106
+ 'Before all: implicit_fail',
107
+ 'After all: implicit_fail'
108
+ ]
66
109
  end
67
110
 
68
111
  it 'Unexpected exception' do
69
112
  action = TestController.action :exception
70
113
  env = Rack::MockRequest.env_for '/exception'
71
-
114
+
72
115
  error = proc { action.call env }.must_raise RuntimeError
73
116
  error.message.must_equal 'Unexpected error'
117
+
118
+ TestController.hooks_log.must_equal [
119
+ 'Before all: exception',
120
+ 'After all: exception'
121
+ ]
74
122
  end
75
123
 
76
124
  end
@@ -8,7 +8,7 @@ describe Rasti::Web::Endpoint do
8
8
  res.must_be_instance_of Rack::Response
9
9
 
10
10
  render.text 'Content'
11
- end
11
+ end
12
12
 
13
13
  env = Rack::MockRequest.env_for '/'
14
14
 
@@ -10,7 +10,7 @@ describe Rasti::Web::Render do
10
10
 
11
11
  it 'Code' do
12
12
  render.status 404
13
-
13
+
14
14
  response.status.must_equal 404
15
15
  response['Content-Type'].must_be_nil
16
16
  response.body.must_equal []
@@ -18,7 +18,7 @@ describe Rasti::Web::Render do
18
18
 
19
19
  it 'Code and body' do
20
20
  render.status 500, 'Internal server error'
21
-
21
+
22
22
  response.status.must_equal 500
23
23
  response['Content-Type'].must_be_nil
24
24
  response.body.must_equal ['Internal server error']
@@ -167,7 +167,7 @@ describe Rasti::Web::Render do
167
167
  end
168
168
 
169
169
  describe 'Javascript' do
170
-
170
+
171
171
  it 'Body' do
172
172
  render.js 'alert("hello");'
173
173
 
@@ -205,7 +205,7 @@ describe Rasti::Web::Render do
205
205
  end
206
206
 
207
207
  describe 'CSS' do
208
-
208
+
209
209
  it 'Body' do
210
210
  render.css 'body{margin:0}'
211
211
 
@@ -250,7 +250,8 @@ describe Rasti::Web::Render do
250
250
  render.file filename
251
251
 
252
252
  response.status.must_equal 200
253
- response['Content-Type'].must_equal 'application/zip'
253
+ response['Content-Type'].must_equal 'application/zip; charset=utf-8'
254
+ response['Content-Disposition'].must_equal 'attachment; filename="sample_file.zip"'
254
255
  response.body.must_equal [File.read(filename)]
255
256
  end
256
257
 
@@ -258,7 +259,8 @@ describe Rasti::Web::Render do
258
259
  render.file filename, 206
259
260
 
260
261
  response.status.must_equal 206
261
- response['Content-Type'].must_equal 'application/zip'
262
+ response['Content-Type'].must_equal 'application/zip; charset=utf-8'
263
+ response['Content-Disposition'].must_equal 'attachment; filename="sample_file.zip"'
262
264
  response.body.must_equal [File.read(filename)]
263
265
  end
264
266
 
@@ -266,7 +268,7 @@ describe Rasti::Web::Render do
266
268
  render.file filename, 'Content-Disposition' => 'attachment; filename=test_file.zip'
267
269
 
268
270
  response.status.must_equal 200
269
- response['Content-Type'].must_equal 'application/zip'
271
+ response['Content-Type'].must_equal 'application/zip; charset=utf-8'
270
272
  response['Content-Disposition'].must_equal 'attachment; filename=test_file.zip'
271
273
  response.body.must_equal [File.read(filename)]
272
274
  end
@@ -275,13 +277,53 @@ describe Rasti::Web::Render do
275
277
  render.file filename, 206, 'Content-Disposition' => 'attachment; filename=test_file.zip'
276
278
 
277
279
  response.status.must_equal 206
278
- response['Content-Type'].must_equal 'application/zip'
280
+ response['Content-Type'].must_equal 'application/zip; charset=utf-8'
279
281
  response['Content-Disposition'].must_equal 'attachment; filename=test_file.zip'
280
282
  response.body.must_equal [File.read(filename)]
281
283
  end
282
284
 
283
285
  end
284
286
 
287
+ describe 'Data' do
288
+
289
+ let(:content) { 'Response data' }
290
+
291
+ it 'Body' do
292
+ render.data content
293
+
294
+ response.status.must_equal 200
295
+ response['Content-Type'].must_be_nil
296
+ response.body.must_equal [content]
297
+ end
298
+
299
+ it 'Body and status' do
300
+ render.data content, 206
301
+
302
+ response.status.must_equal 206
303
+ response['Content-Type'].must_be_nil
304
+ response.body.must_equal [content]
305
+ end
306
+
307
+ it 'Body and headers' do
308
+ render.data content, Rasti::Web::Headers.for_file('test_file.txt')
309
+
310
+ response.status.must_equal 200
311
+ response['Content-Type'].must_equal 'text/plain; charset=utf-8'
312
+ response['Content-Disposition'].must_equal 'attachment; filename="test_file.txt"'
313
+ response.body.must_equal [content]
314
+ end
315
+
316
+ it 'Body, status and headers' do
317
+ render.data content, 206, Rasti::Web::Headers.for_file('test_file.txt')
318
+
319
+ response.status.must_equal 206
320
+ response['Content-Type'].must_equal 'text/plain; charset=utf-8'
321
+ response['Content-Disposition'].must_equal 'attachment; filename="test_file.txt"'
322
+ response.body.must_equal [content]
323
+ end
324
+
325
+ end
326
+
285
327
  it 'Partial' do
286
328
  render.partial 'context_and_locals', title: 'Welcome', text: 'Hello world'
287
329
 
@@ -291,7 +333,7 @@ describe Rasti::Web::Render do
291
333
  end
292
334
 
293
335
  describe 'Layout' do
294
-
336
+
295
337
  it 'Default' do
296
338
  render.layout { 'Page content' }
297
339
 
@@ -322,7 +364,7 @@ describe Rasti::Web::Render do
322
364
 
323
365
  it 'Default layout' do
324
366
  render.view 'context_and_locals', title: 'Welcome', text: 'Hello world'
325
-
367
+
326
368
  response.status.must_equal 200
327
369
  response['Content-Type'].must_equal 'text/html; charset=utf-8'
328
370
  response.body.must_equal ['<html><body><h1>Welcome</h1><div>Hello world</div></body></html>']
@@ -330,7 +372,7 @@ describe Rasti::Web::Render do
330
372
 
331
373
  it 'Custom layout' do
332
374
  render.view 'context_and_locals', {title: 'Welcome', text: 'Hello world'}, 'custom_layout'
333
-
375
+
334
376
  response.status.must_equal 200
335
377
  response['Content-Type'].must_equal 'text/html; charset=utf-8'
336
378
  response.body.must_equal ['<html><body class="custom"><h1>Welcome</h1><div>Hello world</div></body></html>']
@@ -1,7 +1,7 @@
1
1
  require 'minitest_helper'
2
2
 
3
3
  describe Rasti::Web::Request do
4
-
4
+
5
5
  it 'Route params' do
6
6
  env = Rack::MockRequest.env_for '/10/20'
7
7
  env[Rasti::Web::ROUTE_PARAMS] = {'lat' => '10', 'lon' => '20'}
@@ -30,24 +30,38 @@ describe Rasti::Web::Request do
30
30
  request.params['lon'].must_equal '20'
31
31
  end
32
32
 
33
- it 'Json body params' do
34
- env = Rack::MockRequest.env_for '/', input: '{"lat": 10, "lon": 20}', 'CONTENT_TYPE' => 'application/json'
33
+ describe 'Json body params' do
35
34
 
36
- request = Rasti::Web::Request.new env
35
+ it 'Valid content type' do
36
+ env = Rack::MockRequest.env_for '/', input: '{"lat": 10, "lon": 20}', 'CONTENT_TYPE' => 'application/json'
37
37
 
38
- request.must_be :json?
39
- request.params[:lat].must_equal 10
40
- request.params['lon'].must_equal 20
41
- end
38
+ request = Rasti::Web::Request.new env
42
39
 
43
- it 'No json body params' do
44
- env = Rack::MockRequest.env_for '/', input: '{"lat": 10, "lon": 20}', 'CONTENT_TYPE' => 'other/type'
40
+ request.json?.must_equal true
41
+ request.params[:lat].must_equal 10
42
+ request.params['lon'].must_equal 20
43
+ end
45
44
 
46
- request = Rasti::Web::Request.new env
45
+ it 'Invalid content type' do
46
+ env = Rack::MockRequest.env_for '/', input: '{"lat": 10, "lon": 20}', 'CONTENT_TYPE' => 'other/type'
47
+
48
+ request = Rasti::Web::Request.new env
49
+
50
+ request.json?.must_equal false
51
+ request.params[:lat].must_be_nil
52
+ request.params['lon'].must_be_nil
53
+ end
54
+
55
+ it 'Undefined content type' do
56
+ env = Rack::MockRequest.env_for '/', input: '{"lat": 10, "lon": 20}'
57
+
58
+ request = Rasti::Web::Request.new env
59
+
60
+ request.json?.must_equal false
61
+ request.params[:lat].must_be_nil
62
+ request.params['lon'].must_be_nil
63
+ end
47
64
 
48
- request.wont_be :json?
49
- request.params[:lat].must_be_nil
50
- request.params['lon'].must_be_nil
51
65
  end
52
66
 
53
67
  end
@@ -3,12 +3,12 @@ require 'minitest_helper'
3
3
  describe Rasti::Web::Route do
4
4
 
5
5
  ROUTES = [
6
- '/',
6
+ '/',
7
7
  '/*/wildcard/action',
8
8
  '/wildcard/*/action',
9
9
  '/wildcard/*/action/:id',
10
10
  '/wildcard/*',
11
- '/resource',
11
+ '/resource',
12
12
  '/resource/:id/:action',
13
13
  '/:resource(/:id(/:action))'
14
14
  ]
@@ -72,7 +72,7 @@ describe Rasti::Web::Route do
72
72
 
73
73
  route.pattern.must_equal '/*/wildcard/action'
74
74
  route.extract_params(path).must_equal wildcard: 'section/sub_section'
75
- route.call({}).must_equal RESPONSE
75
+ route.call({}).must_equal RESPONSE
76
76
  end
77
77
 
78
78
  it 'Middle' do
@@ -97,7 +97,7 @@ describe Rasti::Web::Route do
97
97
 
98
98
  it 'Params' do
99
99
  path = '/wildcard/section/sub_section/action/123'
100
-
100
+
101
101
  route = route_for path
102
102
 
103
103
  route.pattern.must_equal '/wildcard/*/action/:id'
@@ -11,7 +11,7 @@ describe Rasti::Web::Router do
11
11
  def post(path)
12
12
  Rack::MockRequest.env_for path, method: :post
13
13
  end
14
-
14
+
15
15
  it 'Verbs' do
16
16
  %w(delete get head options patch post put).each do |verb|
17
17
  router.must_respond_to verb
@@ -5,7 +5,7 @@ describe Rasti::Web::Template do
5
5
  class Context
6
6
  include ContextMethodHelper
7
7
  end
8
-
8
+
9
9
  it 'Plain HTML' do
10
10
  Rasti::Web::Template.render('plain_html').must_equal '<div>Hello world</div>'
11
11
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rasti-web
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 2.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gabriel Naiman
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-08-18 00:00:00.000000000 Z
11
+ date: 2020-10-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rack
@@ -44,14 +44,14 @@ dependencies:
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '2.0'
47
+ version: '3.0'
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: '2.0'
54
+ version: '3.0'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: class_config
57
57
  requirement: !ruby/object:Gem::Requirement
@@ -108,20 +108,6 @@ dependencies:
108
108
  - - "~>"
109
109
  - !ruby/object:Gem::Version
110
110
  version: '0.1'
111
- - !ruby/object:Gem::Dependency
112
- name: bundler
113
- requirement: !ruby/object:Gem::Requirement
114
- requirements:
115
- - - "~>"
116
- - !ruby/object:Gem::Version
117
- version: '1.12'
118
- type: :development
119
- prerelease: false
120
- version_requirements: !ruby/object:Gem::Requirement
121
- requirements:
122
- - - "~>"
123
- - !ruby/object:Gem::Version
124
- version: '1.12'
125
111
  - !ruby/object:Gem::Dependency
126
112
  name: rake
127
113
  requirement: !ruby/object:Gem::Requirement
@@ -143,6 +129,9 @@ dependencies:
143
129
  - - "~>"
144
130
  - !ruby/object:Gem::Version
145
131
  version: '5.0'
132
+ - - "<"
133
+ - !ruby/object:Gem::Version
134
+ version: '5.11'
146
135
  type: :development
147
136
  prerelease: false
148
137
  version_requirements: !ruby/object:Gem::Requirement
@@ -150,6 +139,9 @@ dependencies:
150
139
  - - "~>"
151
140
  - !ruby/object:Gem::Version
152
141
  version: '5.0'
142
+ - - "<"
143
+ - !ruby/object:Gem::Version
144
+ version: '5.11'
153
145
  - !ruby/object:Gem::Dependency
154
146
  name: minitest-colorin
155
147
  requirement: !ruby/object:Gem::Requirement
@@ -255,6 +247,7 @@ files:
255
247
  - lib/rasti/web/application.rb
256
248
  - lib/rasti/web/controller.rb
257
249
  - lib/rasti/web/endpoint.rb
250
+ - lib/rasti/web/headers.rb
258
251
  - lib/rasti/web/render.rb
259
252
  - lib/rasti/web/request.rb
260
253
  - lib/rasti/web/route.rb
@@ -299,8 +292,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
299
292
  - !ruby/object:Gem::Version
300
293
  version: '0'
301
294
  requirements: []
302
- rubyforge_project:
303
- rubygems_version: 2.5.1
295
+ rubygems_version: 3.0.6
304
296
  signing_key:
305
297
  specification_version: 4
306
298
  summary: Web blocks to build robust applications