rasti-web 2.0.1 → 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
2
  SHA256:
3
- metadata.gz: d5105988210acdb104ba5ad0088ab6b79083d43e27f29769a77d12c23c8af81f
4
- data.tar.gz: 4d046ecf2410d04a1a655575c502d8c9b335f761820137ee96ff04fe51319484
3
+ metadata.gz: 250a8b04a93b51665f10b49206453b490ffc83c81b8684ca466d4259ea23e75a
4
+ data.tar.gz: 9c32ed8ab0506cb4a4b407bb50fa6f12dc19bafc457e05181f18ba16ad9c5032
5
5
  SHA512:
6
- metadata.gz: 455368d0bd93f5c0b0a385a31165ecc089247d2c33359a328302556cb88201719c7103ded8a647cfbeda87badb280057e820d75eb9c178d7488c9f86375c6bb1
7
- data.tar.gz: 8ab0436ee72e1a20f267cc2f7f87be68bffe46ce6438a870381ab94e4551e708c581841d1eda67d9de60445a5f2e598081d4c48e9f1499d87c2fdb6826bd08d3
6
+ metadata.gz: f30ddbfed0f369d8eb9bcabe5e43b2a9891b9a53d3be55bbe3b4d492cf1724984e25b6a1ca91285879bedc14b8ec691525a345bf62dff48cc72f2d47eab80a8f
7
+ data.tar.gz: 120e466b7433332064adcc6ebfaf8cfc337433221954fddeb867a91cc801e53e8856a1b00a331c6867a1845d3c248f9febe1ac24a4e1864d3cd08ec511d3326d
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
 
@@ -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
@@ -19,7 +19,7 @@ module Rasti
19
19
 
20
20
  def action(action_name)
21
21
  raise "Undefined action '#{action_name}' in #{name}" unless instance_methods.include? action_name.to_sym
22
-
22
+
23
23
  Endpoint.new do |req, res, render|
24
24
  controller = new req, res, render
25
25
  begin
@@ -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
 
@@ -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.nil? && 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 = '2.0.1'
3
+ VERSION = '2.1.0'
4
4
  end
5
5
  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,7 +1,7 @@
1
1
  require 'minitest_helper'
2
2
 
3
3
  class TestController < Rasti::Web::Controller
4
-
4
+
5
5
  CustomError = Class.new StandardError
6
6
 
7
7
  def self.hooks_log
@@ -16,14 +16,14 @@ class TestController < Rasti::Web::Controller
16
16
  self.class.hooks_log << "After all: #{action_name}"
17
17
  end
18
18
 
19
- before_action :test do
19
+ before_action :test do
20
20
  self.class.hooks_log << 'Before single: test'
21
21
  end
22
22
 
23
- after_action :test do
23
+ after_action :test do
24
24
  self.class.hooks_log << 'After single: test'
25
25
  end
26
-
26
+
27
27
  def test
28
28
  render.html 'Test HTML'
29
29
  end
@@ -55,7 +55,7 @@ describe Rasti::Web::Controller do
55
55
  before do
56
56
  TestController.hooks_log.clear
57
57
  end
58
-
58
+
59
59
  it 'Action endpoint' do
60
60
  action = TestController.action :test
61
61
  env = Rack::MockRequest.env_for '/test'
@@ -65,7 +65,7 @@ describe Rasti::Web::Controller do
65
65
  status.must_equal 200
66
66
  headers['Content-Type'].must_equal 'text/html; charset=utf-8'
67
67
  response.body.must_equal ['Test HTML']
68
-
68
+
69
69
  TestController.hooks_log.must_equal [
70
70
  'Before single: test',
71
71
  'After single: test'
@@ -84,6 +84,7 @@ describe Rasti::Web::Controller do
84
84
  status, headers, response = action.call env
85
85
 
86
86
  status.must_equal 500
87
+ headers['Content-Type'].must_be_nil
87
88
  response.body.must_equal ['Explicit error']
88
89
 
89
90
  TestController.hooks_log.must_equal [
@@ -98,6 +99,7 @@ describe Rasti::Web::Controller do
98
99
  status, headers, response = action.call env
99
100
 
100
101
  status.must_equal 500
102
+ headers['Content-Type'].must_be_nil
101
103
  response.body.must_equal ['Implicit error']
102
104
 
103
105
  TestController.hooks_log.must_equal [
@@ -109,7 +111,7 @@ describe Rasti::Web::Controller do
109
111
  it 'Unexpected exception' do
110
112
  action = TestController.action :exception
111
113
  env = Rack::MockRequest.env_for '/exception'
112
-
114
+
113
115
  error = proc { action.call env }.must_raise RuntimeError
114
116
  error.message.must_equal 'Unexpected error'
115
117
 
@@ -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'}
@@ -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: 2.0.1
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: 2020-03-27 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
@@ -247,6 +247,7 @@ files:
247
247
  - lib/rasti/web/application.rb
248
248
  - lib/rasti/web/controller.rb
249
249
  - lib/rasti/web/endpoint.rb
250
+ - lib/rasti/web/headers.rb
250
251
  - lib/rasti/web/render.rb
251
252
  - lib/rasti/web/request.rb
252
253
  - lib/rasti/web/route.rb