eldr 0.0.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 +7 -0
- data/.rspec +3 -0
- data/.rubocop.yml +28 -0
- data/.rubocop_todo.yml +15 -0
- data/.travis.yml +3 -0
- data/DUCK.md +15 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +89 -0
- data/LICENSE +20 -0
- data/README.md +876 -0
- data/Rakefile +9 -0
- data/TODOS +3 -0
- data/eldr.gemspec +36 -0
- data/examples/README.md +7 -0
- data/examples/action_objects.ru +28 -0
- data/examples/app.ru +75 -0
- data/examples/builder.ru +24 -0
- data/examples/custom_response.ru +23 -0
- data/examples/errors.ru +16 -0
- data/examples/hello_world.ru +8 -0
- data/examples/inheritance.ru +22 -0
- data/examples/multiple_apps.ru +30 -0
- data/examples/rails_style_routing.ru +14 -0
- data/examples/rendering_templates.ru +38 -0
- data/examples/views/cats.slim +1 -0
- data/lib/eldr/app.rb +146 -0
- data/lib/eldr/builder.rb +60 -0
- data/lib/eldr/configuration.rb +37 -0
- data/lib/eldr/matcher.rb +36 -0
- data/lib/eldr/recognizer.rb +35 -0
- data/lib/eldr/route.rb +92 -0
- data/lib/eldr/version.rb +3 -0
- data/lib/eldr.rb +1 -0
- data/spec/app_spec.rb +194 -0
- data/spec/builder_spec.rb +11 -0
- data/spec/configuration_spec.rb +29 -0
- data/spec/examples/action_objects_spec.rb +18 -0
- data/spec/examples/builder_spec.rb +16 -0
- data/spec/examples/custom_response_spec.rb +17 -0
- data/spec/examples/errors_spec.rb +18 -0
- data/spec/examples/example_app_spec.rb +98 -0
- data/spec/examples/hello_world_spec.rb +17 -0
- data/spec/examples/inheritance_spec.rb +23 -0
- data/spec/examples/multiple_apps_spec.rb +31 -0
- data/spec/examples/rails_style_routing_spec.rb +17 -0
- data/spec/examples/rendering_templates_spec.rb +26 -0
- data/spec/matcher_spec.rb +21 -0
- data/spec/readme_definitions.yml +28 -0
- data/spec/readme_spec.rb +117 -0
- data/spec/recognizer_spec.rb +32 -0
- data/spec/route_spec.rb +131 -0
- data/spec/spec_helper.rb +14 -0
- metadata +252 -0
data/Rakefile
ADDED
data/TODOS
ADDED
data/eldr.gemspec
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/eldr/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = 'eldr'
|
6
|
+
s.version = Eldr::VERSION
|
7
|
+
s.platform = Gem::Platform::RUBY
|
8
|
+
s.authors = ['K-2052']
|
9
|
+
s.email = ['k@2052.me']
|
10
|
+
s.homepage = 'https://github.com/eldr-rb/eldr'
|
11
|
+
s.summary = 'A minimal framework that lights a fire in your development'
|
12
|
+
s.description = 'A minimal framework that lights a fire in your development.
|
13
|
+
Built close to Rack and the metal.'
|
14
|
+
s.licenses = ['MIT']
|
15
|
+
|
16
|
+
s.required_rubygems_version = '~> 1.3.6'
|
17
|
+
s.required_ruby_version = '~> 2.0'
|
18
|
+
s.rubyforge_project = 'eldr'
|
19
|
+
|
20
|
+
s.add_dependency 'rack', '~> 1.5'
|
21
|
+
s.add_dependency 'mustermann', '0.4.0'
|
22
|
+
s.add_dependency 'fast_blank', '0.0.2'
|
23
|
+
|
24
|
+
s.add_development_dependency 'bundler', '~> 1.7'
|
25
|
+
s.add_development_dependency 'rake', '10.4.2'
|
26
|
+
s.add_development_dependency 'rspec', '3.1.0'
|
27
|
+
s.add_development_dependency 'rubocop', '0.28.0'
|
28
|
+
s.add_development_dependency 'rack-test', '0.6.2'
|
29
|
+
s.add_development_dependency 'tilt', '2.0.1'
|
30
|
+
s.add_development_dependency 'slim', '3.0.1'
|
31
|
+
s.add_development_dependency 'coveralls', '~> 0.7'
|
32
|
+
|
33
|
+
s.files = `git ls-files`.split("\n")
|
34
|
+
s.executables = `git ls-files`.split("\n").map{|f| f =~ /^bin\/(.*)/ ? $1 : nil}.compact
|
35
|
+
s.require_path = 'lib'
|
36
|
+
end
|
data/examples/README.md
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
|
2
|
+
require 'eldr'
|
3
|
+
|
4
|
+
class Show
|
5
|
+
attr_accessor :env
|
6
|
+
|
7
|
+
def helper_logic
|
8
|
+
# do things here
|
9
|
+
end
|
10
|
+
|
11
|
+
def params
|
12
|
+
env['eldr.params']
|
13
|
+
end
|
14
|
+
|
15
|
+
def call(env)
|
16
|
+
@env = env
|
17
|
+
|
18
|
+
helper_logic
|
19
|
+
# @cat = Cat.find params[:id]
|
20
|
+
Rack::Response.new "Found cat named #{params['name'].capitalize}!"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class CatsController < Eldr::App
|
25
|
+
get '/cats/:name', Show.new
|
26
|
+
end
|
27
|
+
|
28
|
+
run CatsController.new
|
data/examples/app.ru
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
class ExampleApp < Eldr::App
|
2
|
+
class NotFound < StandardError
|
3
|
+
def call(env)
|
4
|
+
[404, {}, ['']]
|
5
|
+
end
|
6
|
+
end
|
7
|
+
attr_accessor :cats
|
8
|
+
|
9
|
+
before :before do
|
10
|
+
@thing1 = "Thing1"
|
11
|
+
end
|
12
|
+
|
13
|
+
before do
|
14
|
+
@thing2 = "Thing2"
|
15
|
+
end
|
16
|
+
|
17
|
+
after :cats do
|
18
|
+
@cats == 'Cats!'
|
19
|
+
end
|
20
|
+
|
21
|
+
get '/posts' do |params|
|
22
|
+
Rack::Response.new "posts", 200
|
23
|
+
end
|
24
|
+
|
25
|
+
post '/posts' do
|
26
|
+
Rack::Response.new "posts", 201
|
27
|
+
end
|
28
|
+
|
29
|
+
put '/posts' do
|
30
|
+
Rack::Response.new "posts", 200
|
31
|
+
end
|
32
|
+
|
33
|
+
delete '/posts' do
|
34
|
+
Rack::Response.new "posts", 201
|
35
|
+
end
|
36
|
+
|
37
|
+
get '/deferred' do
|
38
|
+
throw :pass
|
39
|
+
end
|
40
|
+
|
41
|
+
get '/deferred' do
|
42
|
+
Rack::Response.new "Deferred!", 200
|
43
|
+
end
|
44
|
+
|
45
|
+
get '/raises-an-error' do
|
46
|
+
raise NotFound, 'Not Found'
|
47
|
+
end
|
48
|
+
|
49
|
+
get '/state' do
|
50
|
+
@state = 1 + @state.to_i
|
51
|
+
[200, {}, [@state]]
|
52
|
+
end
|
53
|
+
|
54
|
+
get '/state-2' do
|
55
|
+
[200, {}, [@state ||= 0]]
|
56
|
+
end
|
57
|
+
|
58
|
+
get '/before', name: :before do
|
59
|
+
[200, {}, [@thing1]]
|
60
|
+
end
|
61
|
+
|
62
|
+
get '/before-all', name: :before do
|
63
|
+
[200, {}, [@thing2]]
|
64
|
+
end
|
65
|
+
|
66
|
+
get '/after' do
|
67
|
+
[200, {}, ["Cats!"]]
|
68
|
+
end
|
69
|
+
|
70
|
+
get '/posts/:id' do
|
71
|
+
[200, {}, [params['id']]]
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
run ExampleApp
|
data/examples/builder.ru
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
|
2
|
+
require 'eldr'
|
3
|
+
|
4
|
+
class SimpleCounterMiddleware
|
5
|
+
def initialize(app)
|
6
|
+
@app = app
|
7
|
+
end
|
8
|
+
|
9
|
+
def call(env)
|
10
|
+
env['eldr.simple_counter'] ||= 0
|
11
|
+
env['eldr.simple_counter'] += 1
|
12
|
+
@app.call(env)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class BuilderExample < Eldr::App
|
17
|
+
use SimpleCounterMiddleware
|
18
|
+
|
19
|
+
get '/' do |env|
|
20
|
+
Rack::Response.new env['eldr.simple_counter'].to_s
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
run BuilderExample
|
@@ -0,0 +1,23 @@
|
|
1
|
+
$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
|
2
|
+
require 'eldr'
|
3
|
+
|
4
|
+
class Response
|
5
|
+
attr_accessor :status, :headers, :body
|
6
|
+
|
7
|
+
def initialize(body, status=200, headers={})
|
8
|
+
@body, @status, @headers = body, status, headers
|
9
|
+
end
|
10
|
+
|
11
|
+
def to_a
|
12
|
+
[@status, @headers, [@body]]
|
13
|
+
end
|
14
|
+
alias_method :to_ary, :to_a
|
15
|
+
end
|
16
|
+
|
17
|
+
class CustomResponseExample < Eldr::App
|
18
|
+
get '/' do
|
19
|
+
Response.new("Hello World Custom!")
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
run CustomResponseExample
|
data/examples/errors.ru
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
|
2
|
+
require 'eldr'
|
3
|
+
|
4
|
+
class ErrorResponse < StandardError
|
5
|
+
def call(env)
|
6
|
+
Rack::Response.new message, 500
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
class ErrorsExample < Eldr::App
|
11
|
+
get '/' do
|
12
|
+
raise ErrorResponse, "Bad Data"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
run ErrorsExample
|
@@ -0,0 +1,22 @@
|
|
1
|
+
class SimpleMiddleware
|
2
|
+
def initialize(app)
|
3
|
+
@app = app
|
4
|
+
end
|
5
|
+
|
6
|
+
def call(env)
|
7
|
+
env['eldr.simple_counter'] ||= 0
|
8
|
+
env['eldr.simple_counter'] += 1
|
9
|
+
@app.call(env)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class TestInheritanceApp < Eldr::App
|
14
|
+
use SimpleCounterMiddleware
|
15
|
+
set :bob, 'what about him?'
|
16
|
+
end
|
17
|
+
|
18
|
+
class InheritedApp < TestInheritanceApp
|
19
|
+
get '/', proc { [200, {}, [env['eldr.simple_counter']]]}
|
20
|
+
end
|
21
|
+
|
22
|
+
run InheritedApp
|
@@ -0,0 +1,30 @@
|
|
1
|
+
$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
|
2
|
+
require 'eldr'
|
3
|
+
|
4
|
+
class HelloWorldExample < Eldr::App
|
5
|
+
get '/' do
|
6
|
+
Rack::Response.new "Hello World EXAMPLE!"
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
class CatsExample < Eldr::App
|
11
|
+
get '/' do
|
12
|
+
Rack::Response.new "Hello Cats!"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class DogsExample < Eldr::App
|
17
|
+
get '/', proc { Rack::Response.new "Hello Dogs!" }
|
18
|
+
end
|
19
|
+
|
20
|
+
map '/' do
|
21
|
+
run HelloWorldExample
|
22
|
+
end
|
23
|
+
|
24
|
+
map '/cats' do
|
25
|
+
run CatsExample
|
26
|
+
end
|
27
|
+
|
28
|
+
map '/dogs' do
|
29
|
+
run DogsExample
|
30
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
|
2
|
+
require 'eldr'
|
3
|
+
|
4
|
+
class Cats
|
5
|
+
def index env
|
6
|
+
Rack::Response.new "Hello from all the Cats!"
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
class RailsStyle < Eldr::App
|
11
|
+
get '/cats', to: 'Cats#index'
|
12
|
+
end
|
13
|
+
|
14
|
+
run RailsStyle
|
@@ -0,0 +1,38 @@
|
|
1
|
+
$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
|
2
|
+
require 'eldr'
|
3
|
+
require 'tilt'
|
4
|
+
require 'slim'
|
5
|
+
|
6
|
+
module RenderingResponses
|
7
|
+
class NotFound < StandardError
|
8
|
+
def call(env)
|
9
|
+
Rack::Response.new message, 404
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
module RenderingHelpers
|
15
|
+
def render(path, resp_code=200)
|
16
|
+
Rack::Response.new Tilt.new(find_template(path)).render(self), resp_code
|
17
|
+
end
|
18
|
+
|
19
|
+
def find_template(path)
|
20
|
+
template = File.join(__dir__, 'views', path)
|
21
|
+
raise RenderingResponses::NotFound, 'Template Not Found' unless File.exists? template
|
22
|
+
template
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
class RenderingTemplatesExample < Eldr::App
|
27
|
+
include RenderingHelpers
|
28
|
+
|
29
|
+
get '/cats' do
|
30
|
+
render 'cats.slim'
|
31
|
+
end
|
32
|
+
|
33
|
+
get '/no-template' do
|
34
|
+
render 'template.slim'
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
run RenderingTemplatesExample
|
@@ -0,0 +1 @@
|
|
1
|
+
h1 Cats!
|
data/lib/eldr/app.rb
ADDED
@@ -0,0 +1,146 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
require 'rack/request'
|
3
|
+
|
4
|
+
require_relative 'route'
|
5
|
+
require_relative 'matcher'
|
6
|
+
require_relative 'recognizer'
|
7
|
+
require_relative 'configuration'
|
8
|
+
require_relative 'builder'
|
9
|
+
|
10
|
+
###
|
11
|
+
# Eldr is a minimal Ruby framework
|
12
|
+
###
|
13
|
+
module Eldr
|
14
|
+
###
|
15
|
+
# The main class all Eldr apps extend
|
16
|
+
###
|
17
|
+
class App # rubocop:disable Metrics/ClassLength
|
18
|
+
attr_accessor :env, :configuration
|
19
|
+
HTTP_VERBS = %w(DELETE GET HEAD OPTIONS PATCH POST PUT)
|
20
|
+
|
21
|
+
class << self
|
22
|
+
attr_accessor :routes, :recognizer, :builder, :before_filters, :after_filters, :configuration
|
23
|
+
extend Forwardable
|
24
|
+
def_delegators :builder, :map, :use
|
25
|
+
|
26
|
+
alias_method :_new, :new
|
27
|
+
def new(*args, &block)
|
28
|
+
builder.run _new(*args, &block)
|
29
|
+
builder
|
30
|
+
end
|
31
|
+
|
32
|
+
def call(env)
|
33
|
+
new.call env
|
34
|
+
end
|
35
|
+
|
36
|
+
def inherited(base)
|
37
|
+
base.builder.use builder.middleware
|
38
|
+
base.configuration.merge!(configuration)
|
39
|
+
end
|
40
|
+
|
41
|
+
def builder
|
42
|
+
@builder ||= Builder.new
|
43
|
+
end
|
44
|
+
|
45
|
+
def configuration
|
46
|
+
@configuration ||= Configuration.new
|
47
|
+
end
|
48
|
+
alias_method :config, :configuration
|
49
|
+
|
50
|
+
def set(key, value)
|
51
|
+
configuration.set(key, value)
|
52
|
+
end
|
53
|
+
|
54
|
+
def before_filters
|
55
|
+
@before_filters ||= { all: [] }
|
56
|
+
end
|
57
|
+
|
58
|
+
def after_filters
|
59
|
+
@after_filters ||= { all: [] }
|
60
|
+
end
|
61
|
+
|
62
|
+
def before(*keys, &block)
|
63
|
+
*keys = [:all] if keys.empty?
|
64
|
+
keys.each do |key|
|
65
|
+
before_filters[key] ||= []
|
66
|
+
before_filters[key] << block
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def after(*keys, &block)
|
71
|
+
*keys = [:all] if keys.empty?
|
72
|
+
keys.each do |key|
|
73
|
+
after_filters[key] ||= []
|
74
|
+
after_filters[key] << block
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def routes
|
79
|
+
@routes ||= { delete: [], get: [], head: [], options: [], patch: [], post: [], put: [] }
|
80
|
+
end
|
81
|
+
|
82
|
+
def recognizer
|
83
|
+
@recognizer ||= Recognizer.new(routes)
|
84
|
+
end
|
85
|
+
|
86
|
+
HTTP_VERBS.each do |verb|
|
87
|
+
define_method verb.downcase.to_sym do |path, *args, &block|
|
88
|
+
handler = Proc.new(&block) if block
|
89
|
+
handler ||= args.pop if args.last.respond_to?(:call)
|
90
|
+
options ||= args.pop if args.last.is_a?(Hash)
|
91
|
+
options ||= {}
|
92
|
+
add(verb: verb.downcase.to_sym, path: path, handler: handler, options: options)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def add(verb: :get, path: '/', options: {}, handler: nil)
|
97
|
+
handler = Proc.new if block_given?
|
98
|
+
route = Route.new(verb: verb, path: path, options: options, handler: handler)
|
99
|
+
|
100
|
+
route.before_filters.push(*before_filters[route.name]) if before_filters.include? route.name
|
101
|
+
route.after_filters.push(*after_filters[route.name]) if after_filters.include? route.name
|
102
|
+
|
103
|
+
routes[verb] << route
|
104
|
+
route
|
105
|
+
end
|
106
|
+
alias_method :<<, :add
|
107
|
+
end
|
108
|
+
|
109
|
+
def initialize(configuration = nil)
|
110
|
+
configuration ||= self.class.configuration
|
111
|
+
@configuration = configuration
|
112
|
+
end
|
113
|
+
|
114
|
+
def call(env)
|
115
|
+
dup.call!(env)
|
116
|
+
end
|
117
|
+
|
118
|
+
def call!(env)
|
119
|
+
@env = env
|
120
|
+
|
121
|
+
recognize(env).each do |route|
|
122
|
+
env['eldr.route'] = route
|
123
|
+
params.merge!(route.params(env['PATH_INFO']))
|
124
|
+
catch(:pass) { return route.call(env, app: self) }
|
125
|
+
end
|
126
|
+
rescue => error
|
127
|
+
if error.respond_to? :call
|
128
|
+
error.call(env)
|
129
|
+
else
|
130
|
+
raise error
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def request
|
135
|
+
env['eldr.request'] ||= Rack::Request.new(env)
|
136
|
+
end
|
137
|
+
|
138
|
+
def params
|
139
|
+
env['eldr.params'] ||= request.params
|
140
|
+
end
|
141
|
+
|
142
|
+
def recognize(env)
|
143
|
+
self.class.recognizer.call(env)
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
data/lib/eldr/builder.rb
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'rack/builder'
|
2
|
+
|
3
|
+
module Eldr
|
4
|
+
class Builder < Rack::Builder
|
5
|
+
attr_accessor :middleware
|
6
|
+
def initialize(default_app = nil, &block)
|
7
|
+
@middleware, @map, @run, @warmup = [], nil, default_app, nil
|
8
|
+
instance_eval(&block) if block_given?
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.app(default_app = nil, &block)
|
12
|
+
new(default_app, &block).to_app
|
13
|
+
end
|
14
|
+
|
15
|
+
def use(middleware, *args, &block)
|
16
|
+
if middleware.is_a? Array
|
17
|
+
@middleware.push(*middleware)
|
18
|
+
else
|
19
|
+
if @map
|
20
|
+
mapping, @map = @map, nil
|
21
|
+
@middleware << proc { |app| generate_map app, mapping }
|
22
|
+
end
|
23
|
+
@middleware << proc { |app| middleware.new(app, *args, &block) }
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def run(app) # rubocop:disable Style/TrivialAccessors
|
28
|
+
@run = app
|
29
|
+
end
|
30
|
+
|
31
|
+
def warmup(prc = nil, &block)
|
32
|
+
@warmup = prc || block
|
33
|
+
end
|
34
|
+
|
35
|
+
def map(path, &block)
|
36
|
+
@map ||= {}
|
37
|
+
@map[path] = block
|
38
|
+
end
|
39
|
+
|
40
|
+
def to_app
|
41
|
+
app = @map ? generate_map(@run, @map) : @run
|
42
|
+
fail 'missing run or map statement' unless app
|
43
|
+
app = @middleware.reverse.inject(app) { |a, e| e[a] }
|
44
|
+
@warmup.call(app) if @warmup
|
45
|
+
app
|
46
|
+
end
|
47
|
+
|
48
|
+
def call(env)
|
49
|
+
to_app.call(env)
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def generate_map(default_app, mapping)
|
55
|
+
mapped = default_app ? { '/' => default_app } : {}
|
56
|
+
mapping.each { |r, b| mapped[r] = self.class.new(default_app, &b).to_app }
|
57
|
+
URLMap.new(mapped)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Eldr
|
2
|
+
###
|
3
|
+
# This a OpenStruct like class.
|
4
|
+
# It wraps a hash and when you hit a method that doesn't exist it tries to pull it from the hash
|
5
|
+
# Probably can break in 100 ways an is dumb, but it works.
|
6
|
+
# And it performs astromnomically better than stuff like Hashie::Mash
|
7
|
+
###
|
8
|
+
class Configuration
|
9
|
+
attr_accessor :table
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
defaults = { lock: false }
|
13
|
+
table.merge!(defaults)
|
14
|
+
end
|
15
|
+
|
16
|
+
def set(key, value)
|
17
|
+
@table[key] = value
|
18
|
+
end
|
19
|
+
|
20
|
+
def table
|
21
|
+
@table ||= {}
|
22
|
+
end
|
23
|
+
|
24
|
+
def merge!(hash)
|
25
|
+
hash = hash.table unless hash.is_a? Hash
|
26
|
+
@table.merge!(hash)
|
27
|
+
end
|
28
|
+
|
29
|
+
def method_missing(method, *args)
|
30
|
+
if !args.empty? # we assume we are setting something
|
31
|
+
set(method.to_s.gsub(/\=$/, '').to_sym, args.pop)
|
32
|
+
else
|
33
|
+
@table[method.to_s.gsub(/\?$/, '').to_sym]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
data/lib/eldr/matcher.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'mustermann'
|
2
|
+
|
3
|
+
module Eldr
|
4
|
+
class Matcher
|
5
|
+
PATH_DELIMITER = '/'.freeze
|
6
|
+
QUERY_PREFIX = '?'.freeze
|
7
|
+
QUERY_DELIMITER = '&'.freeze
|
8
|
+
|
9
|
+
def initialize(path, options = {})
|
10
|
+
@path = path.is_a?(String) && path.empty? ? PATH_DELIMITER : path
|
11
|
+
@capture = options.delete(:capture)
|
12
|
+
@default_values = options.delete(:default_values)
|
13
|
+
end
|
14
|
+
|
15
|
+
def match(pattern)
|
16
|
+
match_data = handler.match(pattern)
|
17
|
+
return match_data if match_data
|
18
|
+
pattern = pattern[0..-2] if mustermann? && pattern != PATH_DELIMITER && pattern.end_with?(PATH_DELIMITER)
|
19
|
+
handler.match(pattern)
|
20
|
+
end
|
21
|
+
|
22
|
+
def mustermann?
|
23
|
+
handler.instance_of?(Mustermann)
|
24
|
+
end
|
25
|
+
|
26
|
+
def handler
|
27
|
+
@handler ||=
|
28
|
+
case @path
|
29
|
+
when String
|
30
|
+
Mustermann.new(@path, capture: @capture)
|
31
|
+
when Regexp
|
32
|
+
/^(?:#{@path})$/
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'fast_blank'
|
2
|
+
|
3
|
+
module Eldr
|
4
|
+
class Recognizer
|
5
|
+
class NotFound < StandardError
|
6
|
+
def call(_env)
|
7
|
+
[404, {}, ['']]
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
attr_accessor :app, :env, :routes, :pattern
|
12
|
+
|
13
|
+
def initialize(routes)
|
14
|
+
@routes = routes
|
15
|
+
end
|
16
|
+
|
17
|
+
def call(env)
|
18
|
+
@env = env
|
19
|
+
|
20
|
+
ret_routes = routes[verb].select { |route| !route.match(pattern).nil? }
|
21
|
+
raise NotFound if ret_routes.empty?
|
22
|
+
ret_routes
|
23
|
+
end
|
24
|
+
|
25
|
+
def pattern
|
26
|
+
@pattern = env['PATH_INFO']
|
27
|
+
@pattern = '/' if @pattern.blank?
|
28
|
+
@pattern
|
29
|
+
end
|
30
|
+
|
31
|
+
def verb
|
32
|
+
env['REQUEST_METHOD'].downcase.to_sym
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|