rasti-web 1.1.0 → 2.1.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 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