jeanine 0.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 +7 -0
- data/lib/jeanine.rb +26 -0
- data/lib/jeanine/app.rb +68 -0
- data/lib/jeanine/callbacks.rb +28 -0
- data/lib/jeanine/core_ext.rb +4 -0
- data/lib/jeanine/core_ext/array.rb +5 -0
- data/lib/jeanine/core_ext/hash.rb +8 -0
- data/lib/jeanine/core_ext/nil_class.rb +5 -0
- data/lib/jeanine/core_ext/string.rb +6 -0
- data/lib/jeanine/environment.rb +18 -0
- data/lib/jeanine/headers.rb +76 -0
- data/lib/jeanine/mimes.rb +33 -0
- data/lib/jeanine/path_proxy.rb +73 -0
- data/lib/jeanine/renderer.rb +130 -0
- data/lib/jeanine/request.rb +64 -0
- data/lib/jeanine/response.rb +49 -0
- data/lib/jeanine/router.rb +38 -0
- data/lib/jeanine/routing.rb +2 -0
- data/lib/jeanine/routing/dsl.rb +60 -0
- data/lib/jeanine/routing/evaluation.rb +96 -0
- data/lib/jeanine/version.rb +3 -0
- data/lib/jeanine/view.rb +4 -0
- metadata +124 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 8d88a21a82401395ddca515402a07e3155939eb0
|
4
|
+
data.tar.gz: e6edce8a4097864466149cf6769765332a868c76
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 17f7d684adeefc7ec56e7244c6c53fddeafb9d037e4a1ee9aab94d8925193a39a95d0a079ba2aad85ae3f5a64b972315a7893f6ced6d07ba7e693923be9cc67e
|
7
|
+
data.tar.gz: 82082d64a1620c947e51aa84d9ada5701825c0525061cd930022b8ef68611338570fc64f20038a4605e48bf1d27578c668ff2923a0ef7d88bc3b55435792877d
|
data/lib/jeanine.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
require 'logger'
|
3
|
+
|
4
|
+
require "jeanine/version"
|
5
|
+
require 'jeanine/core_ext'
|
6
|
+
require 'jeanine/router'
|
7
|
+
require 'jeanine/app'
|
8
|
+
require 'tilt'
|
9
|
+
|
10
|
+
module Jeanine
|
11
|
+
def self.tilt_cache
|
12
|
+
@title_cache ||= Tilt::Cache.new
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.logger
|
16
|
+
@logger ||= Logger.new(STDOUT)
|
17
|
+
end
|
18
|
+
|
19
|
+
def logger=(val)
|
20
|
+
@logger = val
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.router
|
24
|
+
@router ||= Router.new
|
25
|
+
end
|
26
|
+
end
|
data/lib/jeanine/app.rb
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'jeanine/callbacks'
|
2
|
+
require 'jeanine/mimes'
|
3
|
+
require 'jeanine/request'
|
4
|
+
require 'jeanine/response'
|
5
|
+
require 'jeanine/renderer'
|
6
|
+
require 'jeanine/routing'
|
7
|
+
|
8
|
+
module Jeanine
|
9
|
+
class App
|
10
|
+
include Routing::Evaluation
|
11
|
+
attr_reader :request, :response
|
12
|
+
class << self
|
13
|
+
include Routing::DSL
|
14
|
+
include Callbacks
|
15
|
+
|
16
|
+
alias :_new :new
|
17
|
+
def new(*args, &block)
|
18
|
+
initialize!
|
19
|
+
stack.run _new(*args, &block)
|
20
|
+
stack
|
21
|
+
end
|
22
|
+
|
23
|
+
def initialize!
|
24
|
+
Mimes.load!
|
25
|
+
end
|
26
|
+
|
27
|
+
def stack
|
28
|
+
@stack ||= Rack::Builder.new
|
29
|
+
end
|
30
|
+
|
31
|
+
def router
|
32
|
+
Jeanine.router
|
33
|
+
end
|
34
|
+
|
35
|
+
def call(env)
|
36
|
+
new.call env
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def call(env)
|
41
|
+
@env = env
|
42
|
+
@request = Jeanine::Request.new(env)
|
43
|
+
@response = Jeanine::Response.new
|
44
|
+
catch(:halt) { route_eval }
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def params
|
50
|
+
@params ||= if @request.format
|
51
|
+
@request.params.merge({ format: @request.format })
|
52
|
+
else
|
53
|
+
@request.params
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def render(*args)
|
58
|
+
@response.action_variables = instance_variables_cache
|
59
|
+
Renderer.new(@response).render(*args)
|
60
|
+
end
|
61
|
+
|
62
|
+
def instance_variables_cache
|
63
|
+
instance_variables.each_with_object({}) do |var, obj|
|
64
|
+
obj[var] = instance_variable_get(var)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Jeanine
|
2
|
+
module Callbacks
|
3
|
+
def _callbacks
|
4
|
+
@_callbacks ||= {
|
5
|
+
before: [],
|
6
|
+
after: [],
|
7
|
+
before_all: [],
|
8
|
+
after_all: []
|
9
|
+
}
|
10
|
+
end
|
11
|
+
|
12
|
+
def _register_callback(type, paths = [], &block)
|
13
|
+
if paths == []
|
14
|
+
_callbacks["#{type}_all".to_sym] << { block: block }
|
15
|
+
else
|
16
|
+
_callbacks[type] << { paths: paths, block: block }
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def before(*paths, &block)
|
21
|
+
_register_callback(:before, paths, &block)
|
22
|
+
end
|
23
|
+
|
24
|
+
def after(*paths, &block)
|
25
|
+
_register_callback(:after, paths, &block)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Broding
|
2
|
+
module Environment
|
3
|
+
def env
|
4
|
+
@_env ||= (ENV["RACK_ENV"].presence || "development")
|
5
|
+
end
|
6
|
+
|
7
|
+
def groups(*groups)
|
8
|
+
hash = groups.extract_options!
|
9
|
+
env = Jeanine.env
|
10
|
+
groups.unshift(:default, env)
|
11
|
+
groups.concat ENV["BRODY_GROUPS"].to_s.split(",")
|
12
|
+
groups.concat hash.map { |k, v| k if v.map(&:to_s).include?(env) }
|
13
|
+
groups.compact!
|
14
|
+
groups.uniq!
|
15
|
+
groups
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Jeanine
|
4
|
+
class Headers
|
5
|
+
CGI_VARIABLES = Set.new(%W[
|
6
|
+
AUTH_TYPE
|
7
|
+
CONTENT_LENGTH
|
8
|
+
CONTENT_TYPE
|
9
|
+
GATEWAY_INTERFACE
|
10
|
+
HTTPS
|
11
|
+
PATH_INFO
|
12
|
+
PATH_TRANSLATED
|
13
|
+
QUERY_STRING
|
14
|
+
REMOTE_ADDR
|
15
|
+
REMOTE_HOST
|
16
|
+
REMOTE_IDENT
|
17
|
+
REMOTE_USER
|
18
|
+
REQUEST_METHOD
|
19
|
+
SCRIPT_NAME
|
20
|
+
SERVER_NAME
|
21
|
+
SERVER_PORT
|
22
|
+
SERVER_PROTOCOL
|
23
|
+
SERVER_SOFTWARE
|
24
|
+
]).freeze
|
25
|
+
|
26
|
+
HTTP_HEADER = /\A[A-Za-z0-9-]+\z/
|
27
|
+
|
28
|
+
include Enumerable
|
29
|
+
|
30
|
+
def initialize(request)
|
31
|
+
@req = request
|
32
|
+
end
|
33
|
+
|
34
|
+
def [](key)
|
35
|
+
@req.get_header env_name(key)
|
36
|
+
end
|
37
|
+
|
38
|
+
def key?(key)
|
39
|
+
@req.has_header? env_name(key)
|
40
|
+
end
|
41
|
+
alias :include? :key?
|
42
|
+
|
43
|
+
DEFAULT = Object.new
|
44
|
+
|
45
|
+
def fetch(key, default = DEFAULT)
|
46
|
+
@req.fetch_header(env_name(key)) do
|
47
|
+
return default unless default == DEFAULT
|
48
|
+
return yield if block_given?
|
49
|
+
raise KeyError, key
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def each(&block)
|
54
|
+
@req.each_header(&block)
|
55
|
+
end
|
56
|
+
|
57
|
+
def to_h
|
58
|
+
obj = {}
|
59
|
+
each do |k,v|
|
60
|
+
obj[k] = v
|
61
|
+
end
|
62
|
+
obj
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
def env_name(key)
|
68
|
+
key = key.to_s
|
69
|
+
if HTTP_HEADER.match?(key)
|
70
|
+
key = key.upcase.tr("-", "_")
|
71
|
+
key = "HTTP_" + key unless CGI_VARIABLES.include?(key)
|
72
|
+
end
|
73
|
+
key
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Jeanine
|
2
|
+
class Mimes
|
3
|
+
MimeTypeNotFound = Class.new(StandardError)
|
4
|
+
MIME_TYPES_INVERTED = Rack::Mime::MIME_TYPES.invert
|
5
|
+
|
6
|
+
def self.load!
|
7
|
+
register(:json, Rack::Mime::MIME_TYPES[".json"])
|
8
|
+
register(:html, Rack::Mime::MIME_TYPES[".html"])
|
9
|
+
register(:text, Rack::Mime::MIME_TYPES[".text"])
|
10
|
+
register(:plain, self.for(:text))
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.register(type, header)
|
14
|
+
mime_types[type] = header
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.for(type)
|
18
|
+
mime_types.fetch(type) do
|
19
|
+
raise(MimeTypeNotFound, "Mime #{type} not registered")
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.mime_types
|
24
|
+
@mime_types ||= {}
|
25
|
+
end
|
26
|
+
|
27
|
+
private_class_method :mime_types
|
28
|
+
|
29
|
+
def initialize(*)
|
30
|
+
raise "Should not be initialiazed"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module Jeanine
|
2
|
+
class PathProxy
|
3
|
+
def initialize(context, pathname, options = {})
|
4
|
+
@context = context
|
5
|
+
@pathname = pathname
|
6
|
+
@options = options
|
7
|
+
end
|
8
|
+
|
9
|
+
def get(path = nil, options = {}, &block)
|
10
|
+
@options.reverse_merge!(options)
|
11
|
+
@context.get(merged_path(path), options, &block)
|
12
|
+
end
|
13
|
+
|
14
|
+
def post(path = nil, options = {}, &block)
|
15
|
+
@options.reverse_merge!(options)
|
16
|
+
@context.post(merged_path(path), options, &block)
|
17
|
+
end
|
18
|
+
|
19
|
+
def patch(path = nil, options = {}, &block)
|
20
|
+
@options.reverse_merge!(options)
|
21
|
+
@context.patch(merged_path(path), options, &block)
|
22
|
+
end
|
23
|
+
|
24
|
+
def put(path = nil, options = {}, &block)
|
25
|
+
@options.reverse_merge!(options)
|
26
|
+
@context.put(merged_path(path), options, &block)
|
27
|
+
end
|
28
|
+
|
29
|
+
def options(path = nil, options = {}, &block)
|
30
|
+
@options.reverse_merge!(options)
|
31
|
+
@context.options(merged_path(path), options, &block)
|
32
|
+
end
|
33
|
+
|
34
|
+
def head(path = nil, options = {}, &block)
|
35
|
+
@options.reverse_merge!(options)
|
36
|
+
@context.head(merged_path(path), options, &block)
|
37
|
+
end
|
38
|
+
|
39
|
+
def delete(path = nil, options = {}, &block)
|
40
|
+
@options.reverse_merge!(options)
|
41
|
+
@context.delete(merged_path(path), options, &block)
|
42
|
+
end
|
43
|
+
|
44
|
+
def path(path = nil, options = {}, &block)
|
45
|
+
@options.reverse_merge!(options)
|
46
|
+
@context.path(merged_path(path), options, &block)
|
47
|
+
end
|
48
|
+
|
49
|
+
def match(path = nil, options = {}, &block)
|
50
|
+
@options.reverse_merge!(options)
|
51
|
+
@context.match(merged_path(path), options, &block)
|
52
|
+
end
|
53
|
+
|
54
|
+
def root(path = '/', options = {}, &block)
|
55
|
+
@options.reverse_merge!(options)
|
56
|
+
@context.root(merged_path(path), options, &block)
|
57
|
+
end
|
58
|
+
|
59
|
+
def before(*paths, &block)
|
60
|
+
@context.before(*paths, &block)
|
61
|
+
end
|
62
|
+
|
63
|
+
def after(*paths, &block)
|
64
|
+
@context.after(*paths, &block)
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
def merged_path(path)
|
70
|
+
[@pathname, path].join
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,130 @@
|
|
1
|
+
require 'jeanine/view'
|
2
|
+
|
3
|
+
module Jeanine
|
4
|
+
class Renderer
|
5
|
+
def self._renderers
|
6
|
+
@_renderers ||= Set.new
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.add(key, &block)
|
10
|
+
define_method(_render_with_renderer_method_name(key), &block)
|
11
|
+
_renderers << key.to_sym
|
12
|
+
end
|
13
|
+
|
14
|
+
def _render_with_renderer_method_name(key)
|
15
|
+
self.class._render_with_renderer_method_name(key)
|
16
|
+
end
|
17
|
+
|
18
|
+
def self._render_with_renderer_method_name(key)
|
19
|
+
"_render_with_renderer_#{key}"
|
20
|
+
end
|
21
|
+
|
22
|
+
def initialize(response)
|
23
|
+
@response = response
|
24
|
+
end
|
25
|
+
|
26
|
+
def render(*args)
|
27
|
+
options = _normalize_render(*args)
|
28
|
+
_render_to_body_with_renderer(options)
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def _normalize_render(*args, &block)
|
34
|
+
options = _normalize_args(*args, &block)
|
35
|
+
_normalize_options(options)
|
36
|
+
options
|
37
|
+
end
|
38
|
+
|
39
|
+
def _normalize_args(action = nil, options = {})
|
40
|
+
if action.is_a?(Hash)
|
41
|
+
action
|
42
|
+
else
|
43
|
+
options
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def _normalize_options(options)
|
48
|
+
options
|
49
|
+
end
|
50
|
+
|
51
|
+
def _render_to_body_with_renderer(options)
|
52
|
+
self.class._renderers.each do |name|
|
53
|
+
if options.key?(name)
|
54
|
+
_process_options(options)
|
55
|
+
method_name = _render_with_renderer_method_name(name)
|
56
|
+
return send(method_name, options.delete(name), options)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
nil
|
60
|
+
end
|
61
|
+
|
62
|
+
def _process_options(options)
|
63
|
+
status, content_type, location = options.values_at(:status, :content_type, :location)
|
64
|
+
@response.status = status if status
|
65
|
+
@response.content_type = content_type if content_type
|
66
|
+
@response.headers["Location"] = location if location
|
67
|
+
end
|
68
|
+
|
69
|
+
add :json do |json, options|
|
70
|
+
json = json.to_json(options) unless json.is_a?(String)
|
71
|
+
|
72
|
+
if options[:callback]
|
73
|
+
"/**/#{options[:callback]}(#{json})"
|
74
|
+
else
|
75
|
+
@response.content_type = Mimes.for(:json)
|
76
|
+
json
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def cache
|
81
|
+
Thread.current[:tilt_cache] ||= Tilt::Cache.new
|
82
|
+
end
|
83
|
+
|
84
|
+
def html(engine, template_name, options = {}, locals = {}, &block)
|
85
|
+
locals = options.delete(:locals) || locals || {}
|
86
|
+
layout = options[:layout]
|
87
|
+
scope = options.delete(:scope) || self
|
88
|
+
options.delete(:layout)
|
89
|
+
options[:outvar] ||= '@_out_buf'
|
90
|
+
options[:default_encoding] ||= "UTF-8"
|
91
|
+
template = compile_template(engine, template_name, options)
|
92
|
+
output = template.render(scope, locals, &block)
|
93
|
+
if layout
|
94
|
+
unless layout.include?("layouts/")
|
95
|
+
layout = "layouts/#{layout}"
|
96
|
+
end
|
97
|
+
options = options.merge(layout: false, scope: scope)
|
98
|
+
catch(:layout_missing) { return html(engine, layout, options, locals) { output } }
|
99
|
+
end
|
100
|
+
output
|
101
|
+
end
|
102
|
+
|
103
|
+
def compile_template(engine, template_name, options)
|
104
|
+
Tilt::Cache.new.fetch engine, template_name, options do
|
105
|
+
template = Tilt[engine]
|
106
|
+
raise "Template engine not found: #{engine}" if template.nil?
|
107
|
+
template.new("views/#{template_name}", options)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
add :template do |template, options|
|
112
|
+
view = Jeanine::View.new
|
113
|
+
@response.action_variables.each do |k,v|
|
114
|
+
view.instance_variable_set(k, v.dup)
|
115
|
+
end
|
116
|
+
options[:scope] = view
|
117
|
+
html(:erb, template, options)
|
118
|
+
end
|
119
|
+
|
120
|
+
add :text do |text, _options|
|
121
|
+
@response.content_type = Mimes.for(:text)
|
122
|
+
text
|
123
|
+
end
|
124
|
+
|
125
|
+
add :plain do |text, _options|
|
126
|
+
@response.content_type = Mimes.for(:plain)
|
127
|
+
text
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'rack/request'
|
2
|
+
require 'jeanine/headers'
|
3
|
+
|
4
|
+
module Jeanine
|
5
|
+
class Request < Rack::Request
|
6
|
+
FORMAT_REGEX = %r{\.(\w+)\z}
|
7
|
+
|
8
|
+
def initialize(env)
|
9
|
+
env['PATH_INFO'] = '/' if env['PATH_INFO'].empty?
|
10
|
+
if env['PATH_INFO'].include?(".")
|
11
|
+
format = env['PATH_INFO'].match(FORMAT_REGEX)
|
12
|
+
if format
|
13
|
+
@format = format.captures[0]
|
14
|
+
env['PATH_INFO'].gsub!(FORMAT_REGEX, '')
|
15
|
+
end
|
16
|
+
end
|
17
|
+
super
|
18
|
+
end
|
19
|
+
|
20
|
+
def headers
|
21
|
+
@headers ||= Jeanine::Headers.new(self)
|
22
|
+
end
|
23
|
+
|
24
|
+
def post?
|
25
|
+
request_method == 'POST'
|
26
|
+
end
|
27
|
+
|
28
|
+
def get?
|
29
|
+
request_method == 'GET'
|
30
|
+
end
|
31
|
+
|
32
|
+
def delete?
|
33
|
+
request_method == 'DELETE'
|
34
|
+
end
|
35
|
+
|
36
|
+
def put?
|
37
|
+
request_method == 'PUT'
|
38
|
+
end
|
39
|
+
|
40
|
+
def patch?
|
41
|
+
request_method == 'PATCH'
|
42
|
+
end
|
43
|
+
|
44
|
+
def options?
|
45
|
+
request_method == 'options'
|
46
|
+
end
|
47
|
+
|
48
|
+
def head?
|
49
|
+
request_method == 'head'
|
50
|
+
end
|
51
|
+
|
52
|
+
def json?
|
53
|
+
format == 'json'
|
54
|
+
end
|
55
|
+
|
56
|
+
def mime_type
|
57
|
+
@mime_type ||= Mimes::MIME_TYPES_INVERTED[content_type]
|
58
|
+
end
|
59
|
+
|
60
|
+
def format
|
61
|
+
@format || Rack::Mime::MIME_TYPES[content_type]
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
module Jeanine
|
5
|
+
class Response
|
6
|
+
attr_accessor :action_variables
|
7
|
+
attr_reader :headers, :body
|
8
|
+
attr_accessor :status
|
9
|
+
|
10
|
+
def initialize(body = [], status = 200, headers = { 'Content-Type' => 'text/html; charset=utf-8' })
|
11
|
+
@body = body
|
12
|
+
@status = status
|
13
|
+
@headers = headers
|
14
|
+
@length = 0
|
15
|
+
|
16
|
+
return if body == []
|
17
|
+
if body.respond_to? :to_str
|
18
|
+
write body.to_str
|
19
|
+
elsif body.respond_to? :each
|
20
|
+
body.each { |i| write i.to_s }
|
21
|
+
else
|
22
|
+
raise TypeError, 'body must #respond_to? #to_str or #each'
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def complete!
|
27
|
+
unless (100..199).include?(status) || status == 204
|
28
|
+
headers['Content-Length'] = @length.to_s
|
29
|
+
end
|
30
|
+
[status, headers, body]
|
31
|
+
end
|
32
|
+
|
33
|
+
def content_type=(type)
|
34
|
+
headers['Content-Type'] = type
|
35
|
+
end
|
36
|
+
|
37
|
+
def redirect_to(target, status = 302)
|
38
|
+
self.status = status
|
39
|
+
headers['Location'] = target
|
40
|
+
end
|
41
|
+
|
42
|
+
def write(string)
|
43
|
+
s = string.to_s
|
44
|
+
@length += s.bytesize
|
45
|
+
|
46
|
+
body << s
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
3
|
+
module Jeanine
|
4
|
+
class Router
|
5
|
+
extend Forwardable
|
6
|
+
|
7
|
+
def_delegators :@routes, :[]
|
8
|
+
|
9
|
+
attr_reader :routes
|
10
|
+
def initialize
|
11
|
+
@routes = {
|
12
|
+
GET: [],
|
13
|
+
POST: [],
|
14
|
+
PATCH: [],
|
15
|
+
PUT: [],
|
16
|
+
DELETE: [],
|
17
|
+
OPTIONS: [],
|
18
|
+
HEAD: [],
|
19
|
+
}
|
20
|
+
end
|
21
|
+
|
22
|
+
def add(verb, path, options = {}, &block)
|
23
|
+
routes[verb] << build_route("#{path}", options, &block)
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def build_route(path, options = {}, &block)
|
29
|
+
route = { path: "#{path}", compiled_path: nil, params: [], block: block }
|
30
|
+
compiled_path = route[:path].gsub(/:\w+/) do |match|
|
31
|
+
route[:params] << match.tr(':', '').to_sym
|
32
|
+
'([^/?#]+)'
|
33
|
+
end
|
34
|
+
route[:compiled_path] = /^#{compiled_path}?$/
|
35
|
+
route
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'jeanine/path_proxy'
|
2
|
+
|
3
|
+
module Jeanine
|
4
|
+
module Routing
|
5
|
+
module DSL
|
6
|
+
extend SingleForwardable
|
7
|
+
|
8
|
+
RouteError = Class.new(StandardError)
|
9
|
+
|
10
|
+
def_delegator :Jeanine, :router
|
11
|
+
|
12
|
+
def get(path = nil, options = {}, &block)
|
13
|
+
router.add(:GET, path, options, &block)
|
14
|
+
end
|
15
|
+
|
16
|
+
def post(path = nil, options = {}, &block)
|
17
|
+
router.add(:POST, path, options, &block)
|
18
|
+
end
|
19
|
+
|
20
|
+
def put(path = nil, options = {}, &block)
|
21
|
+
router.add(:PUT, path, options, &block)
|
22
|
+
end
|
23
|
+
|
24
|
+
def patch(path = nil, options = {}, &block)
|
25
|
+
router.add(:PATCH, path, options, &block)
|
26
|
+
end
|
27
|
+
|
28
|
+
def head(path = nil, options = {}, &block)
|
29
|
+
router.add(:HEAD, path, options, &block)
|
30
|
+
end
|
31
|
+
|
32
|
+
def options(path = nil, options = {}, &block)
|
33
|
+
router.add(:OPTIONS, path, options, &block)
|
34
|
+
end
|
35
|
+
|
36
|
+
def delete(path = nil, options = {}, &block)
|
37
|
+
router.add(:DELETE, path, options, &block)
|
38
|
+
end
|
39
|
+
|
40
|
+
def path(pathname, options = {}, &block)
|
41
|
+
option_merger = Jeanine::PathProxy.new(self, pathname, options)
|
42
|
+
option_merger.instance_eval(&block)
|
43
|
+
end
|
44
|
+
|
45
|
+
def root(path = "/", options = {}, &block)
|
46
|
+
router.add(:GET, path, options, &block)
|
47
|
+
end
|
48
|
+
|
49
|
+
def match(path = nil, options = {}, &block)
|
50
|
+
via = options.delete(:via)
|
51
|
+
unless via.is_a?(Array)
|
52
|
+
raise RouteError, "options[:via] must be an array of HTTP verbs"
|
53
|
+
end
|
54
|
+
via.each do |verb|
|
55
|
+
router.add(verb.upcase, path, options, &block)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
module Jeanine
|
2
|
+
module Routing
|
3
|
+
module Evaluation
|
4
|
+
IGNORED_INSTANCE_VARIABLES = [:@env]
|
5
|
+
def route_eval
|
6
|
+
before_find_route!
|
7
|
+
route = find_route
|
8
|
+
|
9
|
+
if route
|
10
|
+
before_evaluate_route!
|
11
|
+
result = instance_eval(&route[:block])
|
12
|
+
@response.write(result)
|
13
|
+
after_evaluate_route!
|
14
|
+
else
|
15
|
+
@response.status = 404
|
16
|
+
end
|
17
|
+
after_response!
|
18
|
+
@response.complete!
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def find_route
|
24
|
+
matches = nil
|
25
|
+
route = Jeanine.router[@request.request_method.to_sym].detect do |r|
|
26
|
+
matches = r[:compiled_path].match(@request.path_info)
|
27
|
+
!matches.nil?
|
28
|
+
end
|
29
|
+
|
30
|
+
return nil if route.nil?
|
31
|
+
return route if route && route[:params].empty?
|
32
|
+
|
33
|
+
index = 0
|
34
|
+
while index < matches.captures.size
|
35
|
+
param = route[:params][index]
|
36
|
+
@request.params[param] = matches.captures[index]
|
37
|
+
if index == match.captures.size
|
38
|
+
@request.params[param].gsub!(@request.format)
|
39
|
+
end
|
40
|
+
index += 1
|
41
|
+
end
|
42
|
+
|
43
|
+
route
|
44
|
+
end
|
45
|
+
|
46
|
+
def before_find_route!
|
47
|
+
run_before_callbacks!(:before_all)
|
48
|
+
end
|
49
|
+
|
50
|
+
def before_evaluate_route!
|
51
|
+
run_before_callbacks!(:before)
|
52
|
+
end
|
53
|
+
|
54
|
+
def after_evaluate_route!
|
55
|
+
run_after_callbacks!(:after)
|
56
|
+
end
|
57
|
+
|
58
|
+
def after_response!
|
59
|
+
run_after_callbacks!(:after_all)
|
60
|
+
end
|
61
|
+
|
62
|
+
def run_before_callbacks!(type)
|
63
|
+
if type == :before_all
|
64
|
+
self.class._callbacks[type].each { |callback| eval_callback(&callback[:block]) }
|
65
|
+
else
|
66
|
+
matching_callbacks(type) do |callback|
|
67
|
+
eval_callback(&callback[:block])
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def run_after_callbacks!(type)
|
73
|
+
if type == :after_all
|
74
|
+
self.class._callbacks[type].each { |callback| eval_callback(&callback[:block]) }
|
75
|
+
else
|
76
|
+
matching_callbacks(type) do |callback|
|
77
|
+
eval_callback(&callback[:block])
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def matching_callbacks(type)
|
83
|
+
self.class._callbacks[type].select do |callback|
|
84
|
+
paths = callback[:paths]
|
85
|
+
if paths.detect { |path| path.match?(@request.path_info) }
|
86
|
+
yield callback
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def eval_callback(*args, &callback)
|
92
|
+
instance_exec(*args, &callback)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
data/lib/jeanine/view.rb
ADDED
metadata
ADDED
@@ -0,0 +1,124 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: jeanine
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Josh Brody
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2020-07-07 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rack
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: tilt
|
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'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rspec
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
description: A framework.
|
70
|
+
email:
|
71
|
+
- git@josh.mn
|
72
|
+
executables: []
|
73
|
+
extensions: []
|
74
|
+
extra_rdoc_files: []
|
75
|
+
files:
|
76
|
+
- lib/jeanine.rb
|
77
|
+
- lib/jeanine/app.rb
|
78
|
+
- lib/jeanine/callbacks.rb
|
79
|
+
- lib/jeanine/core_ext.rb
|
80
|
+
- lib/jeanine/core_ext/array.rb
|
81
|
+
- lib/jeanine/core_ext/hash.rb
|
82
|
+
- lib/jeanine/core_ext/nil_class.rb
|
83
|
+
- lib/jeanine/core_ext/string.rb
|
84
|
+
- lib/jeanine/environment.rb
|
85
|
+
- lib/jeanine/headers.rb
|
86
|
+
- lib/jeanine/mimes.rb
|
87
|
+
- lib/jeanine/path_proxy.rb
|
88
|
+
- lib/jeanine/renderer.rb
|
89
|
+
- lib/jeanine/request.rb
|
90
|
+
- lib/jeanine/response.rb
|
91
|
+
- lib/jeanine/router.rb
|
92
|
+
- lib/jeanine/routing.rb
|
93
|
+
- lib/jeanine/routing/dsl.rb
|
94
|
+
- lib/jeanine/routing/evaluation.rb
|
95
|
+
- lib/jeanine/version.rb
|
96
|
+
- lib/jeanine/view.rb
|
97
|
+
homepage: https://github.com/joshmn/jeanine
|
98
|
+
licenses:
|
99
|
+
- MIT
|
100
|
+
metadata:
|
101
|
+
homepage_uri: https://github.com/joshmn/jeanine
|
102
|
+
source_code_uri: https://github.com/joshmn/jeanine
|
103
|
+
changelog_uri: https://github.com/joshmn/jeanine
|
104
|
+
post_install_message:
|
105
|
+
rdoc_options: []
|
106
|
+
require_paths:
|
107
|
+
- lib
|
108
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
109
|
+
requirements:
|
110
|
+
- - ">="
|
111
|
+
- !ruby/object:Gem::Version
|
112
|
+
version: 2.3.0
|
113
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
requirements: []
|
119
|
+
rubyforge_project:
|
120
|
+
rubygems_version: 2.6.14.3
|
121
|
+
signing_key:
|
122
|
+
specification_version: 4
|
123
|
+
summary: A framework.
|
124
|
+
test_files: []
|