/
endpoint.rb
150 lines (139 loc) · 3.75 KB
/
endpoint.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
require 'delegate'
require 'lotus/utils/class'
module Lotus
module Routing
# Endpoint not found
# This is raised when the router fails to load an endpoint at the runtime.
#
# @since 0.1.0
class EndpointNotFound < ::StandardError
end
# Routing endpoint
# This is the object that responds to an HTTP request made against a certain
# path.
#
# The router will use this class for:
#
# * Procs and any Rack compatible object (respond to #call)
#
# @since 0.1.0
#
# @api private
#
# @example
# require 'lotus/router'
#
# Lotus::Router.new do
# get '/proc', to: ->(env) { [200, {}, ['This will use Lotus::Routing::Endpoint']] }
# get '/rack-app', to: RackApp.new
# end
class Endpoint < SimpleDelegator
# @since x.x.x
def inspect
case __getobj__
when Proc
source, line = __getobj__.source_location
lambda_inspector = " (lambda)" if __getobj__.lambda?
"#<Proc@#{ source }:#{ line }#{ lambda_inspector }>"
when Class
__getobj__
else
"#<#{ __getobj__.class }>"
end
end
end
# Routing endpoint
# This is the object that responds to an HTTP request made against a certain
# path.
#
# The router will use this class for:
#
# * Classes
# * Lotus::Action endpoints referenced as a class
# * Lotus::Action endpoints referenced a string
# * RESTful resource(s)
#
# @since 0.1.0
#
# @api private
#
# @example
# require 'lotus/router'
#
# Lotus::Router.new do
# get '/class', to: RackMiddleware
# get '/lotus-action-class', to: DashboardController::Index
# get '/lotus-action-string', to: 'dashboard#index'
#
# resource 'identity'
# resources 'articles'
# end
class ClassEndpoint < Endpoint
# Rack interface
#
# @since 0.1.0
def call(env)
__getobj__.new.call(env)
end
end
# Routing endpoint
# This is the object that responds to an HTTP request made against a certain
# path.
#
# The router will use this class for the same use cases of `ClassEndpoint`,
# but when the target class can't be found, instead of raise a `LoadError`
# we reference in a lazy endpoint.
#
# For each incoming HTTP request, it will look for the referenced class,
# then it will instantiate and invoke #call on the object.
#
# This behavior is required to solve a chicken-egg situation when we try
# to load the router first and then the application with all its endpoints.
#
# @since 0.1.0
#
# @api private
#
# @see Lotus::Routing::ClassEndpoint
class LazyEndpoint < Endpoint
# Initialize the lazy endpoint
#
# @since 0.1.0
def initialize(name, namespace)
@name, @namespace = name, namespace
end
# Rack interface
#
# @raise [EndpointNotFound] when the endpoint can't be found.
#
# @since 0.1.0
def call(env)
obj.call(env)
end
# @since x.x.x
def inspect
# TODO review this implementation once the namespace feature will be
# cleaned up.
result = klass rescue nil
if result.nil?
result = @name
result = "#{ @namespace }::#{ result }" if @namespace != Object
end
result
end
private
# @since 0.1.0
# @api private
def obj
klass.new
end
# @since x.x.x
# @api private
def klass
Utils::Class.load!(@name, @namespace)
rescue NameError => e
raise EndpointNotFound.new(e.message)
end
end
end
end