angelo 0.1.1 → 0.1.2

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
  SHA1:
3
- metadata.gz: c49fed8026c10ee370aea064b5be14c7a38848b1
4
- data.tar.gz: b56899e2f4ac2310c414b09bb2838357e223df6c
3
+ metadata.gz: 667a3b95c269095d00f41d03cadfe1e3cc40f404
4
+ data.tar.gz: 9143b9fee78b9b06591516f147a29ce147169447
5
5
  SHA512:
6
- metadata.gz: bfcc2c164ab9d1908ff4f5c0557c115395d3ab48bd92c530d5040cac4e29c469c4a718cf15206e746d78da12d891bc01555a493b359b481bfed2c85987370a11
7
- data.tar.gz: a586ff3c8271e29e7475b1c26a796240a0854e86f3c0449e7f099da02c95e725b0dd298623d9ecd7058bc4465c8061cee468cbf75bf12de32523ad35a64eca91
6
+ metadata.gz: 85107d56bb0446a5611a53eec5fd91b737f5e27362345bd9d87370d7183639da3254aa3101709297f9c7fcd90d15563e597de418255fcf14090a5bbb7b91c369
7
+ data.tar.gz: 8e248165128d31c5f4e891cf74d7508d982fc9798def4fea2fa022e6bf24fbcf77ce87b5d6b12c7e3e1eeef071bf24f123064386433ebbf03d1199b5c285fa3e
data/CHANGELOG.md CHANGED
@@ -1,6 +1,20 @@
1
1
  changelog
2
2
  =========
3
3
 
4
+ ### 0.1.2 5 mar 2014 burnout, denial
5
+
6
+ * basic static file support (i.e. /public files)
7
+ * basic ETag/If-None-Match support
8
+
9
+ ### 0.1.1 3 mar 2014
10
+
11
+ * fix for params with no Content-Type header
12
+
13
+ ### 0.1.0 24 feb 2014
14
+
15
+ * fix for socket paths with mustermann
16
+ * sorta common log-ish-ness
17
+
4
18
  ### 0.0.9 20 feb 2014
5
19
 
6
20
  * memoize params
data/Gemfile CHANGED
@@ -2,6 +2,7 @@ source 'https://rubygems.org'
2
2
 
3
3
  gem 'reel'
4
4
  gem 'tilt'
5
+ gem 'mime-types'
5
6
 
6
7
  platform :rbx do
7
8
  gem 'rubysl-cgi'
data/angelo.gemspec CHANGED
@@ -13,4 +13,5 @@ Gem::Specification.new do |gem|
13
13
  gem.version = Angelo::VERSION
14
14
  gem.license = 'apache'
15
15
  gem.add_dependency 'reel'
16
+ gem.add_dependency 'mime-types'
16
17
  end
data/lib/angelo/base.rb CHANGED
@@ -32,10 +32,15 @@ module Angelo
32
32
  end
33
33
 
34
34
  def subclass.view_dir
35
- v = self.class_variable_get(:@@views) rescue 'views'
35
+ v = self.class_variable_get(:@@views) rescue DEFAULT_VIEW_DIR
36
36
  File.join root, v
37
37
  end
38
38
 
39
+ def subclass.public_dir
40
+ p = self.class_variable_get(:@@public_dir) rescue DEFAULT_PUBLIC_DIR
41
+ File.join root, p
42
+ end
43
+
39
44
  end
40
45
 
41
46
  def compile! name, &block
@@ -36,7 +36,7 @@ module Angelo
36
36
  end
37
37
  private :hc_req
38
38
 
39
- [:get, :post, :put, :delete, :options].each do |m|
39
+ [:get, :post, :put, :delete, :options, :head].each do |m|
40
40
  define_method m do |path, params = {}, headers = {}|
41
41
  hc_req m, path, params, headers
42
42
  end
data/lib/angelo/server.rb CHANGED
@@ -1,3 +1,5 @@
1
+ require 'openssl'
2
+
1
3
  module Angelo
2
4
 
3
5
  class Server < Reel::Server
@@ -14,13 +16,21 @@ module Angelo
14
16
  # RubyProf.resume
15
17
  connection.each_request do |request|
16
18
  meth = request.websocket? ? :socket : request.method.downcase.to_sym
17
- route! meth, connection, request
19
+ dispatch! meth, connection, request
18
20
  end
19
21
  # RubyProf.pause
20
22
  end
21
23
 
22
24
  private
23
25
 
26
+ def dispatch! meth, connection, request
27
+ if staticable?(meth) and lp = local_path(request.path)
28
+ static! meth, connection, request, lp
29
+ else
30
+ route! meth, connection, request
31
+ end
32
+ end
33
+
24
34
  def route! meth, connection, request
25
35
  rs = @base.routes[meth][request.path]
26
36
  if rs
@@ -34,6 +44,54 @@ module Angelo
34
44
  end
35
45
  end
36
46
 
47
+ def local_path path
48
+ if @base.public_dir
49
+ lp = File.join(@base.public_dir, path)
50
+ File.file?(lp) ? lp : nil
51
+ end
52
+ end
53
+
54
+ def staticable? meth
55
+ STATICABLE.include? meth
56
+ end
57
+
58
+ def static! meth, connection, request, local_path
59
+ etag = etag_for local_path
60
+ if request.headers[IF_NONE_MATCH_HEADER_KEY] == etag
61
+ Angelo.log connection, request, nil, :not_modified, 0
62
+ connection.respond :not_modified
63
+ else
64
+ headers = {
65
+
66
+ # Content-Type
67
+ #
68
+ CONTENT_TYPE_HEADER_KEY =>
69
+ (MIME::Types.type_for(File.extname(local_path))[0].content_type rescue HTML_TYPE),
70
+
71
+ # Content-Disposition
72
+ #
73
+ CONTENT_DISPOSITION_HEADER_KEY =>
74
+ DEFAULT_CONTENT_DISPOSITION + "; filename=#{File.basename local_path}",
75
+
76
+ # Content-Length
77
+ #
78
+ CONTENT_LENGTH_HEADER_KEY => File.size(local_path),
79
+
80
+ # ETag
81
+ #
82
+ ETAG_HEADER_KEY => etag
83
+
84
+ }
85
+ Angelo.log connection, request, nil, :ok, headers[CONTENT_LENGTH_HEADER_KEY]
86
+ connection.respond :ok, headers, (meth == :head ? nil : File.read(local_path))
87
+ end
88
+ end
89
+
90
+ def etag_for local_path
91
+ fs = File::Stat.new local_path
92
+ OpenSSL::Digest::SHA.hexdigest fs.ino.to_s + fs.size.to_s + fs.mtime.to_s
93
+ end
94
+
37
95
  end
38
96
 
39
97
  end
@@ -1,3 +1,3 @@
1
1
  module Angelo
2
- VERSION = '0.1.1'
2
+ VERSION = '0.1.2'
3
3
  end
data/lib/angelo.rb CHANGED
@@ -16,16 +16,26 @@ module Angelo
16
16
 
17
17
  ROUTABLE = [:get, :post, :put, :delete, :socket]
18
18
  HTTPABLE = [:get, :post, :put, :delete]
19
+ STATICABLE = [:get, :head]
19
20
 
20
21
  CONTENT_TYPE_HEADER_KEY = 'Content-Type'
22
+ CONTENT_DISPOSITION_HEADER_KEY = 'Content-Disposition'
23
+ CONTENT_LENGTH_HEADER_KEY = 'Content-Length'
24
+ DEFAULT_CONTENT_DISPOSITION = 'attachment'
25
+ ETAG_HEADER_KEY = 'ETag'
26
+ IF_NONE_MATCH_HEADER_KEY = 'If-None-Match'
21
27
 
22
28
  HTML_TYPE = 'text/html'
23
29
  JSON_TYPE = 'application/json'
24
30
  FORM_TYPE = 'application/x-www-form-urlencoded'
31
+ FILE_TYPE = 'application/octet-stream'
25
32
 
26
33
  DEFAULT_ADDR = '127.0.0.1'
27
34
  DEFAULT_PORT = 4567
28
35
 
36
+ DEFAULT_VIEW_DIR = 'views'
37
+ DEFAULT_PUBLIC_DIR = 'public'
38
+
29
39
  DEFAULT_RESPONSE_HEADERS = {
30
40
  CONTENT_TYPE_HEADER_KEY => HTML_TYPE
31
41
  }
@@ -1,8 +1,6 @@
1
1
  require_relative '../spec_helper'
2
2
  require 'angelo/tilt/erb'
3
3
 
4
- ROOT = File.expand_path '..', __FILE__
5
-
6
4
  describe Angelo::Base do
7
5
  describe Angelo::Tilt::ERB do
8
6
 
@@ -10,7 +8,7 @@ describe Angelo::Base do
10
8
 
11
9
  include Angelo::Tilt::ERB
12
10
 
13
- @root = ROOT
11
+ @root = TEST_APP_ROOT
14
12
 
15
13
  def set_vars
16
14
  @title = 'test'
@@ -3,8 +3,6 @@ if RUBY_VERSION =~ /^2\./
3
3
  require_relative '../spec_helper'
4
4
  require 'angelo/mustermann'
5
5
 
6
- TILT_MM_TEST_ROOT = File.expand_path '..', __FILE__
7
-
8
6
  describe Angelo::Mustermann do
9
7
 
10
8
  describe 'pattern matching' do
@@ -63,7 +61,7 @@ if RUBY_VERSION =~ /^2\./
63
61
  include Angelo::Tilt::ERB
64
62
  include Angelo::Mustermann
65
63
 
66
- @root = TILT_MM_TEST_ROOT
64
+ @root = TEST_APP_ROOT
67
65
 
68
66
  get '/:foo/things/:bar' do
69
67
  @title = params[:foo]
@@ -0,0 +1,59 @@
1
+ require_relative '../spec_helper'
2
+ require 'openssl'
3
+
4
+ describe Angelo::Server do
5
+
6
+ describe 'serving static files' do
7
+
8
+ let(:test_css_etag) do
9
+ fs = File::Stat.new File.join(TEST_APP_ROOT, 'public', 'test.css')
10
+ OpenSSL::Digest::SHA.hexdigest fs.ino.to_s + fs.size.to_s + fs.mtime.to_s
11
+ end
12
+
13
+ define_app do
14
+
15
+ @root = TEST_APP_ROOT
16
+
17
+ get '/test.html' do
18
+ 'you should not see this'
19
+ end
20
+
21
+ end
22
+
23
+ it 'serves static files for gets' do
24
+ get '/test.css'
25
+ expect(last_response.status).to be(200)
26
+ expect(last_response.headers['Content-Type']).to eq('text/css')
27
+ expect(last_response.headers['Content-Disposition']).to eq('attachment; filename=test.css')
28
+ expect(last_response.headers['Content-Length']).to eq('116')
29
+ expect(last_response.headers['Etag']).to eq(test_css_etag)
30
+ expect(last_response.body.length).to be(116)
31
+ expect(last_response.body).to eq(File.read(File.join TEST_APP_ROOT, 'public', 'test.css'))
32
+ end
33
+
34
+ it 'serves headers for static files on head' do
35
+ head '/test.css'
36
+ expect(last_response.status).to be(200)
37
+ expect(last_response.headers['Content-Type']).to eq('text/css')
38
+ expect(last_response.headers['Content-Disposition']).to eq('attachment; filename=test.css')
39
+ expect(last_response.headers['Content-Length']).to eq('116')
40
+ expect(last_response.headers['Etag']).to eq(test_css_etag)
41
+ expect(last_response.body.length).to be(0)
42
+ end
43
+
44
+ it 'serves static file over route' do
45
+ get '/test.html'
46
+ expect(last_response.status).to be(200)
47
+ expect(last_response.headers['Content-Type']).to eq('text/html')
48
+ expect(last_response.headers['Content-Disposition']).to eq('attachment; filename=test.html')
49
+ expect(last_response.body).to eq(File.read(File.join TEST_APP_ROOT, 'public', 'test.html'))
50
+ end
51
+
52
+ it 'not modifieds when if-none-match matched etag' do
53
+ get '/test.css', {}, {'If-None-Match' => test_css_etag}
54
+ expect(last_response.status).to be(304)
55
+ end
56
+
57
+ end
58
+
59
+ end
data/spec/spec_helper.rb CHANGED
@@ -6,3 +6,5 @@ require 'angelo'
6
6
  require 'angelo/rspec/helpers'
7
7
  Celluloid.logger.level = ::Logger::ERROR
8
8
  include Angelo::RSpec::Helpers
9
+
10
+ TEST_APP_ROOT = File.expand_path '../test_app_root', __FILE__
@@ -0,0 +1,6 @@
1
+ body {
2
+ font-family: arial, helvetica;
3
+ font-size: 10em;
4
+ background-color: #000000;
5
+ color: #ff4ff5;
6
+ }
@@ -0,0 +1 @@
1
+ this is a different story
@@ -0,0 +1 @@
1
+ setTimeout(function(){ alert('hi'); }, 3000);
Binary file
File without changes
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: angelo
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kenichi Nakamura
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-03-03 00:00:00.000000000 Z
11
+ date: 2014-03-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: reel
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - '>='
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: mime-types
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
27
41
  description: A Sinatra-esque DSL for Reel
28
42
  email:
29
43
  - kenichi.nakamura@gmail.com
@@ -51,11 +65,16 @@ files:
51
65
  - spec/angelo/erb_spec.rb
52
66
  - spec/angelo/mustermann_spec.rb
53
67
  - spec/angelo/params_spec.rb
54
- - spec/angelo/views/index.html.erb
55
- - spec/angelo/views/layout.html.erb
68
+ - spec/angelo/static_spec.rb
56
69
  - spec/angelo/websocket_spec.rb
57
70
  - spec/angelo_spec.rb
58
71
  - spec/spec_helper.rb
72
+ - spec/test_app_root/public/test.css
73
+ - spec/test_app_root/public/test.html
74
+ - spec/test_app_root/public/test.js
75
+ - spec/test_app_root/public/what.png
76
+ - spec/test_app_root/views/index.html.erb
77
+ - spec/test_app_root/views/layout.html.erb
59
78
  homepage: https://github.com/kenichi/angelo
60
79
  licenses:
61
80
  - apache
@@ -84,9 +103,13 @@ test_files:
84
103
  - spec/angelo/erb_spec.rb
85
104
  - spec/angelo/mustermann_spec.rb
86
105
  - spec/angelo/params_spec.rb
87
- - spec/angelo/views/index.html.erb
88
- - spec/angelo/views/layout.html.erb
106
+ - spec/angelo/static_spec.rb
89
107
  - spec/angelo/websocket_spec.rb
90
108
  - spec/angelo_spec.rb
91
109
  - spec/spec_helper.rb
92
- has_rdoc:
110
+ - spec/test_app_root/public/test.css
111
+ - spec/test_app_root/public/test.html
112
+ - spec/test_app_root/public/test.js
113
+ - spec/test_app_root/public/what.png
114
+ - spec/test_app_root/views/index.html.erb
115
+ - spec/test_app_root/views/layout.html.erb