angelo 0.1.1 → 0.1.2

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
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