lydia 0.1.3 → 0.1.4
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 +4 -4
- data/.rubocop.yml +61 -0
- data/.travis.yml +3 -8
- data/README.md +93 -22
- data/Rakefile +3 -3
- data/bin/console +3 -3
- data/examples/{example2.rb → config.ru} +2 -0
- data/examples/hello_world.rb +1 -1
- data/lib/lydia.rb +2 -2
- data/lib/lydia/application.rb +8 -11
- data/lib/lydia/delegator.rb +2 -2
- data/lib/lydia/filters.rb +11 -12
- data/lib/lydia/halted.rb +1 -1
- data/lib/lydia/helpers.rb +5 -5
- data/lib/lydia/not_found.rb +1 -1
- data/lib/lydia/request.rb +2 -2
- data/lib/lydia/response.rb +37 -25
- data/lib/lydia/route.rb +30 -22
- data/lib/lydia/router.rb +29 -30
- data/lib/lydia/standard_pages.rb +10 -7
- data/lib/lydia/version.rb +1 -1
- data/lydia.gemspec +9 -5
- data/spec/application_spec.rb +16 -16
- data/spec/delegator_spec.rb +5 -9
- data/spec/filters_spec.rb +16 -18
- data/spec/helpers_spec.rb +14 -14
- data/spec/middleware_spec.rb +9 -9
- data/spec/response_spec.rb +36 -38
- data/spec/router_spec.rb +18 -19
- data/spec/spec_helper.rb +1 -1
- data/spec/templates_spec.rb +5 -5
- metadata +35 -6
data/lib/lydia/halted.rb
CHANGED
data/lib/lydia/helpers.rb
CHANGED
@@ -3,17 +3,17 @@ module Lydia
|
|
3
3
|
def content_type(content)
|
4
4
|
@response.header['Content-Type'] = content
|
5
5
|
end
|
6
|
-
|
6
|
+
|
7
7
|
def redirect(target, status = 302)
|
8
8
|
[status, target]
|
9
9
|
end
|
10
|
-
|
10
|
+
|
11
11
|
def params
|
12
12
|
@request.params
|
13
13
|
end
|
14
|
-
|
14
|
+
|
15
15
|
def send_file(_path, _mime_type = nil)
|
16
|
-
raise
|
16
|
+
raise(NotImplementedError, 'Send file not yet implemented.')
|
17
17
|
end
|
18
18
|
end
|
19
|
-
end
|
19
|
+
end
|
data/lib/lydia/not_found.rb
CHANGED
data/lib/lydia/request.rb
CHANGED
data/lib/lydia/response.rb
CHANGED
@@ -2,46 +2,58 @@ require 'rack/response'
|
|
2
2
|
require 'json'
|
3
3
|
|
4
4
|
module Lydia
|
5
|
-
class Response < Rack::Response
|
5
|
+
class Response < Rack::Response
|
6
6
|
def initialize(*)
|
7
7
|
super
|
8
8
|
headers['Content-Type'] = 'text/html' if headers['Content-Type'].nil?
|
9
9
|
end
|
10
|
-
|
10
|
+
|
11
11
|
def build(input)
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
write input.last.is_a?(Array) ? input.last[0] : input.last
|
18
|
-
headers.merge!(input[1]) if input.count == 3
|
19
|
-
when 'Fixnum'
|
20
|
-
@status = input
|
21
|
-
when 'Hash'
|
22
|
-
headers['Content-Type'] = 'application/json'
|
23
|
-
write input.to_json
|
24
|
-
else
|
25
|
-
if input.respond_to?(:each)
|
26
|
-
write input
|
27
|
-
else
|
28
|
-
raise ArgumentError.new("#{input.class} is not a valid allowed return type")
|
29
|
-
end
|
12
|
+
input_class = input.class.to_s.downcase
|
13
|
+
if %w(string array fixnum hash).include?(input_class)
|
14
|
+
send("build_#{input_class}", input)
|
15
|
+
else
|
16
|
+
build_default(input)
|
30
17
|
end
|
31
18
|
finish
|
32
19
|
end
|
33
|
-
|
20
|
+
|
34
21
|
def finish(&block)
|
35
22
|
@block = block
|
36
|
-
|
37
23
|
if [204, 205, 304].include?(status.to_i)
|
38
|
-
headers.delete
|
39
|
-
headers.delete
|
24
|
+
headers.delete('Content-Length')
|
25
|
+
headers.delete('Content-Type')
|
40
26
|
close
|
41
27
|
[status.to_i, header, []]
|
42
28
|
else
|
43
29
|
[status.to_i, header, @body]
|
44
30
|
end
|
45
|
-
end
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def build_string(input)
|
36
|
+
write(input)
|
37
|
+
end
|
38
|
+
|
39
|
+
def build_array(input)
|
40
|
+
@status, *, body = input
|
41
|
+
write(body.is_a?(Array) ? body[0] : body)
|
42
|
+
headers.merge!(input[1]) if input.count == 3
|
43
|
+
end
|
44
|
+
|
45
|
+
def build_fixnum(input)
|
46
|
+
@status = input
|
47
|
+
end
|
48
|
+
|
49
|
+
def build_hash(input)
|
50
|
+
headers['Content-Type'] = 'application/json'
|
51
|
+
write(input.to_json)
|
52
|
+
end
|
53
|
+
|
54
|
+
def build_default(input)
|
55
|
+
return write(input) if input.respond_to?(:each)
|
56
|
+
raise(ArgumentError, "#{input.class} is not a valid allowed return type")
|
57
|
+
end
|
46
58
|
end
|
47
59
|
end
|
data/lib/lydia/route.rb
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
module Lydia
|
2
|
-
class Route
|
2
|
+
class Route
|
3
3
|
attr_reader :regexp, :params, :request_method, :namespace, :pattern, :options, :block
|
4
|
-
|
5
|
-
WILDCARD_REGEX =
|
6
|
-
NAMED_SEGMENTS_REGEX =
|
4
|
+
|
5
|
+
WILDCARD_REGEX = %r{\/\*(.*)}
|
6
|
+
NAMED_SEGMENTS_REGEX = %r{\/([^\/]*):([^:$\/]+)}
|
7
7
|
|
8
8
|
def initialize(request_method, namespace, pattern, options = {}, &block)
|
9
9
|
@request_method = request_method
|
@@ -11,27 +11,35 @@ module Lydia
|
|
11
11
|
@pattern = pattern
|
12
12
|
@options = options
|
13
13
|
@block = block
|
14
|
-
|
15
|
-
path = (namespace || '') + pattern
|
16
|
-
if path.match(WILDCARD_REGEX)
|
17
|
-
result = path.gsub(WILDCARD_REGEX, '(?:/(.*)|)')
|
18
|
-
elsif path.match(NAMED_SEGMENTS_REGEX)
|
19
|
-
result = path.gsub(NAMED_SEGMENTS_REGEX, '/\1(?<\2>[^.$/]+)')
|
20
|
-
else
|
21
|
-
result = path
|
22
|
-
end
|
23
|
-
@regexp = Regexp.new("\\A#{result}\\z")
|
24
|
-
elsif pattern.is_a?(Regexp)
|
25
|
-
@regexp = Regexp.new((namespace || '') + pattern.to_s)
|
26
|
-
else
|
27
|
-
raise ArgumentError.new('Pattern must be a string or a regex')
|
28
|
-
end
|
14
|
+
@regexp = init_regexp
|
29
15
|
end
|
30
16
|
|
31
17
|
def match?(env)
|
32
|
-
match = @regexp.match(
|
33
|
-
|
18
|
+
match = @regexp.match((env['PATH_INFO']).to_s)
|
19
|
+
if match && match.names.size
|
20
|
+
@params = Hash[match.names.map(&:to_sym).zip(match.captures)]
|
21
|
+
end
|
34
22
|
match
|
35
23
|
end
|
36
|
-
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def init_regexp
|
28
|
+
return regexp_from_string if @pattern.is_a? String
|
29
|
+
return Regexp.new((@namespace || '') + @pattern.to_s) if @pattern.is_a?(Regexp)
|
30
|
+
raise(ArgumentError, 'Pattern must be a string or a regex')
|
31
|
+
end
|
32
|
+
|
33
|
+
def regexp_from_string
|
34
|
+
path = (@namespace || '') + @pattern
|
35
|
+
result = if path.match(WILDCARD_REGEX)
|
36
|
+
path.gsub(WILDCARD_REGEX, '(?:/(.*)|)')
|
37
|
+
elsif path.match(NAMED_SEGMENTS_REGEX)
|
38
|
+
path.gsub(NAMED_SEGMENTS_REGEX, '/\1(?<\2>[^.$/]+)')
|
39
|
+
else
|
40
|
+
path
|
41
|
+
end
|
42
|
+
Regexp.new("\\A#{result}\\z")
|
43
|
+
end
|
44
|
+
end
|
37
45
|
end
|
data/lib/lydia/router.rb
CHANGED
@@ -8,20 +8,20 @@ require 'rack/response'
|
|
8
8
|
module Lydia
|
9
9
|
class Router
|
10
10
|
include StandardPages
|
11
|
-
|
11
|
+
|
12
12
|
attr_reader :request, :response, :env
|
13
|
-
|
14
|
-
class << self
|
13
|
+
|
14
|
+
class << self
|
15
15
|
def routes
|
16
16
|
@routes ||= Hash.new { |h, k| h[k] = [] }
|
17
17
|
end
|
18
|
-
|
18
|
+
|
19
19
|
%w(HEAD GET PATCH PUT POST DELETE OPTIONS).each do |request_method|
|
20
20
|
define_method(request_method.downcase) do |pattern, options = {}, &block|
|
21
21
|
routes[request_method] << Route.new(request_method, @namespace, pattern, options, &block)
|
22
22
|
end
|
23
23
|
end
|
24
|
-
|
24
|
+
|
25
25
|
def namespace(pattern, _options = {})
|
26
26
|
prev_namespace = @namespace ||= ''
|
27
27
|
@namespace += pattern
|
@@ -29,58 +29,57 @@ module Lydia
|
|
29
29
|
@namespace = prev_namespace
|
30
30
|
end
|
31
31
|
end
|
32
|
-
|
32
|
+
|
33
33
|
def next_route
|
34
34
|
throw :next_route
|
35
|
-
end
|
36
|
-
|
35
|
+
end
|
36
|
+
|
37
37
|
def halt(input = nil)
|
38
|
-
raise
|
38
|
+
raise(Halted.new(input || halted), 'Halt')
|
39
39
|
end
|
40
|
-
|
40
|
+
|
41
41
|
def call(env)
|
42
42
|
dup._call(env)
|
43
43
|
end
|
44
|
-
|
44
|
+
|
45
45
|
def _call(env)
|
46
46
|
@env = env
|
47
47
|
@request = new_request(env)
|
48
48
|
@response = new_response
|
49
49
|
process
|
50
50
|
end
|
51
|
-
|
51
|
+
|
52
52
|
def new_request(env)
|
53
53
|
Rack::Request.new(env)
|
54
54
|
end
|
55
|
-
|
55
|
+
|
56
56
|
def new_response(body = [], status = 200, header = {})
|
57
57
|
Rack::Response.new(body, status, header)
|
58
58
|
end
|
59
|
-
|
59
|
+
|
60
60
|
def process
|
61
61
|
dispatch(env)
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
62
|
+
rescue NotFound
|
63
|
+
not_found(env)
|
64
|
+
rescue Halted => exception
|
65
|
+
exception.content
|
66
|
+
rescue StandardError => exception
|
67
|
+
internal_server_error(env, exception)
|
68
68
|
end
|
69
|
-
|
69
|
+
|
70
70
|
def routes
|
71
71
|
self.class.routes
|
72
72
|
end
|
73
|
-
|
73
|
+
|
74
74
|
def dispatch(env)
|
75
75
|
routes[env['REQUEST_METHOD']].each do |route|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
end
|
76
|
+
next unless route.match?(env)
|
77
|
+
@request.params.merge!(route.params) if route.params
|
78
|
+
catch(:next_route) do
|
79
|
+
return instance_eval(&route.block)
|
81
80
|
end
|
82
81
|
end
|
83
|
-
raise
|
84
|
-
end
|
82
|
+
raise(NotFound, 'Not found')
|
83
|
+
end
|
85
84
|
end
|
86
|
-
end
|
85
|
+
end
|
data/lib/lydia/standard_pages.rb
CHANGED
@@ -1,18 +1,21 @@
|
|
1
|
-
module Lydia
|
1
|
+
module Lydia
|
2
2
|
module StandardPages
|
3
3
|
def not_found(env = nil)
|
4
|
-
message =
|
5
|
-
|
4
|
+
message = '<html><body>'\
|
5
|
+
'<h1>Not Found</h1>'\
|
6
|
+
"<p>No route matches #{env['REQUEST_METHOD']} #{env['PATH_INFO']}</p>"\
|
7
|
+
'</body></html>'
|
8
|
+
[404, { 'Content-Type' => 'text/html', 'Content-Length' => message.length.to_s }, [message]]
|
6
9
|
end
|
7
10
|
|
8
11
|
def internal_server_error(_env = nil, exception = nil)
|
9
12
|
message = "<html><body><h1>Internal server error</h1><p>#{exception}</p></body></html>"
|
10
|
-
[500, {'Content-Type' => 'text/html', 'Content-Length' => message.length.to_s}, [message]]
|
13
|
+
[500, { 'Content-Type' => 'text/html', 'Content-Length' => message.length.to_s }, [message]]
|
11
14
|
end
|
12
|
-
|
15
|
+
|
13
16
|
def halted(_env = nil)
|
14
|
-
message =
|
15
|
-
[500, {'Content-Type' => 'text/html', 'Content-Length' => message.length.to_s}, [message]]
|
17
|
+
message = '<html><body><h1>Application halted</h1></body></html>'
|
18
|
+
[500, { 'Content-Type' => 'text/html', 'Content-Length' => message.length.to_s }, [message]]
|
16
19
|
end
|
17
20
|
end
|
18
21
|
end
|
data/lib/lydia/version.rb
CHANGED
data/lydia.gemspec
CHANGED
@@ -9,12 +9,14 @@ Gem::Specification.new do |spec|
|
|
9
9
|
spec.authors = ['Mirko Mignini']
|
10
10
|
spec.email = ['mirko.mignini@gmail.com']
|
11
11
|
|
12
|
-
spec.summary =
|
13
|
-
spec.description =
|
12
|
+
spec.summary = 'Lightweight, fast and easy to use small ruby web framework.'
|
13
|
+
spec.description = 'Lightweight, fast and easy to use small ruby web framework.'
|
14
14
|
spec.homepage = 'https://github.com/MirkoMignini/lydia'
|
15
15
|
spec.license = 'MIT'
|
16
16
|
|
17
|
-
spec.files
|
17
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
18
|
+
f.match(%r{^(test|spec|features)/})
|
19
|
+
end
|
18
20
|
spec.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
19
21
|
spec.bindir = 'exe'
|
20
22
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
@@ -26,7 +28,9 @@ Gem::Specification.new do |spec|
|
|
26
28
|
spec.add_development_dependency 'rake'
|
27
29
|
spec.add_development_dependency 'rack-test'
|
28
30
|
spec.add_development_dependency 'haml'
|
29
|
-
|
30
|
-
spec.
|
31
|
+
spec.add_development_dependency 'rubocop'
|
32
|
+
spec.add_development_dependency 'rubocop-rspec'
|
33
|
+
|
34
|
+
spec.add_dependency 'rack', '~> 2.0.1'
|
31
35
|
spec.add_dependency 'tilt'
|
32
36
|
end
|
data/spec/application_spec.rb
CHANGED
@@ -4,7 +4,7 @@ require 'rack/response'
|
|
4
4
|
require 'erb'
|
5
5
|
require 'lydia/application'
|
6
6
|
|
7
|
-
describe
|
7
|
+
describe 'Application' do
|
8
8
|
include Rack::Test::Methods
|
9
9
|
|
10
10
|
class API < Lydia::Application
|
@@ -12,30 +12,30 @@ describe "Application" do
|
|
12
12
|
'Api call'
|
13
13
|
end
|
14
14
|
end
|
15
|
-
|
16
|
-
class TestApplication < Lydia::Application
|
15
|
+
|
16
|
+
class TestApplication < Lydia::Application
|
17
17
|
use Rack::Lint
|
18
18
|
|
19
19
|
map '/api' do
|
20
20
|
run API.new
|
21
21
|
end
|
22
|
-
|
22
|
+
|
23
23
|
get '/response' do
|
24
24
|
respond_to?(:response).to_s
|
25
|
-
end
|
26
|
-
|
25
|
+
end
|
26
|
+
|
27
27
|
get '/empty' do
|
28
28
|
end
|
29
|
-
|
29
|
+
|
30
30
|
get '/rack_response' do
|
31
31
|
Rack::Response.new(['Rack response'])
|
32
32
|
end
|
33
33
|
end
|
34
|
-
|
34
|
+
|
35
35
|
def app
|
36
36
|
TestApplication.new
|
37
37
|
end
|
38
|
-
|
38
|
+
|
39
39
|
context 'Composition' do
|
40
40
|
it 'GET /api/users' do
|
41
41
|
get '/api/users'
|
@@ -43,24 +43,24 @@ describe "Application" do
|
|
43
43
|
expect(last_response.body).to eq('Api call')
|
44
44
|
end
|
45
45
|
end
|
46
|
-
|
47
|
-
context 'Response' do
|
46
|
+
|
47
|
+
context 'Response' do
|
48
48
|
it 'Response is handled' do
|
49
49
|
get '/response'
|
50
50
|
expect(last_response.status).to eq(200)
|
51
51
|
expect(last_response.body).to eq('true')
|
52
52
|
end
|
53
|
-
|
53
|
+
|
54
54
|
it 'Returns empty' do
|
55
55
|
get '/empty'
|
56
56
|
expect(last_response.status).to eq(200)
|
57
|
-
expect(last_response.body).to eq('')
|
57
|
+
expect(last_response.body).to eq('')
|
58
58
|
end
|
59
|
-
|
59
|
+
|
60
60
|
it 'Returns Rack::Response' do
|
61
61
|
get '/rack_response'
|
62
62
|
expect(last_response.status).to eq(200)
|
63
|
-
expect(last_response.body).to eq('Rack response')
|
63
|
+
expect(last_response.body).to eq('Rack response')
|
64
64
|
end
|
65
65
|
end
|
66
|
-
end
|
66
|
+
end
|