jetra 1.0.1
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/jetra.rb +2 -0
- data/lib/jetra/adapter/rack.rb +66 -0
- data/lib/jetra/application.rb +10 -0
- data/lib/jetra/base.rb +323 -0
- data/lib/jetra/builder.rb +51 -0
- data/lib/jetra/combiner.rb +53 -0
- data/lib/jetra/middleware/sample.rb +30 -0
- data/lib/jetra/middleware/validater.rb +37 -0
- data/lib/jetra/version.rb +5 -0
- metadata +53 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 5b6d7a10300cb071943c84db41627c7dbc051e4c
|
4
|
+
data.tar.gz: f4c8b2ce7efaa9d93ccbe301eb70ee51b44b155f
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 000a5c59fc536a6d0f95ffef0c50bee342da2843da07f2cc288f9c1d7245eb6fe0ee40712f96ccfdf8b7abe1e1d7036705c6f70fc210aaf7311fd6eb907555f6
|
7
|
+
data.tar.gz: d7c93df3cba8b1f6e734c01f6499c50457a02a4b2dbd1c87330fc6cb2585bb7d97fc618ede00926b1c62ad4f061cd19a13c8510f3421859c43e330da2825ab7b
|
data/lib/jetra.rb
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
|
2
|
+
require "set"
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
module Jetra
|
6
|
+
|
7
|
+
class RackAdapter
|
8
|
+
|
9
|
+
include Rack::Utils
|
10
|
+
|
11
|
+
def initialize(app)
|
12
|
+
@app = app
|
13
|
+
|
14
|
+
@routes = Set.new
|
15
|
+
@app.routes.each_key do |route|
|
16
|
+
@routes << route
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
def call(env)
|
22
|
+
|
23
|
+
request = Rack::Request.new(env)
|
24
|
+
|
25
|
+
route = request.path_info
|
26
|
+
route.chop! if (char=route[-1]) and char=='/' # ignore last '/' char
|
27
|
+
route[0] = '' if route[0]=="/" #remove first '/' char
|
28
|
+
|
29
|
+
sym_route = route.to_sym
|
30
|
+
|
31
|
+
if @routes.include?(sym_route)
|
32
|
+
params = indifferent_params(request.params)
|
33
|
+
res = @app.call(sym_route, params)
|
34
|
+
else
|
35
|
+
res = @app.call(:not_found, {route: route})
|
36
|
+
end
|
37
|
+
|
38
|
+
result = {}
|
39
|
+
result[:status] = res.status
|
40
|
+
result[:body] = res.body
|
41
|
+
|
42
|
+
['200', {'Content-Type' => 'application/json;charset=utf-8'}, [result.to_json]]
|
43
|
+
end
|
44
|
+
|
45
|
+
# Enable string or symbol key access to the nested params hash.
|
46
|
+
def indifferent_params(object)
|
47
|
+
case object
|
48
|
+
when Hash
|
49
|
+
new_hash = indifferent_hash
|
50
|
+
object.each { |key, value| new_hash[key] = indifferent_params(value) }
|
51
|
+
new_hash
|
52
|
+
when Array
|
53
|
+
object.map { |item| indifferent_params(item) }
|
54
|
+
else
|
55
|
+
object
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# Creates a Hash with indifferent access.
|
60
|
+
def indifferent_hash
|
61
|
+
Hash.new {|hash,key| hash[key.to_s] if Symbol === key }
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
data/lib/jetra/base.rb
ADDED
@@ -0,0 +1,323 @@
|
|
1
|
+
require "jetra/application"
|
2
|
+
|
3
|
+
module Jetra
|
4
|
+
|
5
|
+
class NotFoundException < Exception ; end
|
6
|
+
|
7
|
+
class Halt ; end
|
8
|
+
|
9
|
+
class Request
|
10
|
+
|
11
|
+
attr_accessor :route, :params
|
12
|
+
|
13
|
+
def initialize(route, params)
|
14
|
+
@route = route || ""
|
15
|
+
@params = params || {}
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class Response
|
20
|
+
|
21
|
+
attr_accessor :status, :body
|
22
|
+
|
23
|
+
def initialize(status=0, body=nil)
|
24
|
+
@status = status.to_i
|
25
|
+
@body = body
|
26
|
+
end
|
27
|
+
|
28
|
+
def finish
|
29
|
+
self.freeze
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class Base
|
34
|
+
|
35
|
+
attr_accessor :request, :response, :params
|
36
|
+
|
37
|
+
def call(route, params)
|
38
|
+
dup.call!(route, params)
|
39
|
+
end
|
40
|
+
|
41
|
+
def call!(route, params)
|
42
|
+
|
43
|
+
@request = Request.new(route, params)
|
44
|
+
@response = Response.new
|
45
|
+
|
46
|
+
@params = indifferentParams(@request.params)
|
47
|
+
|
48
|
+
invoke { dispatch! }
|
49
|
+
|
50
|
+
@response.finish
|
51
|
+
end
|
52
|
+
|
53
|
+
def currentClass
|
54
|
+
self.class
|
55
|
+
end
|
56
|
+
|
57
|
+
def indifferentParams(object)
|
58
|
+
case object
|
59
|
+
when Hash
|
60
|
+
newHash = indifferentHash
|
61
|
+
object.each { |key, value| newHash[key] = indifferentParams(value) }
|
62
|
+
newHash
|
63
|
+
when Array
|
64
|
+
object.map { |item| indifferentParams(item) }
|
65
|
+
else
|
66
|
+
object
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def indifferentHash
|
71
|
+
Hash.new {|hash,key| hash[key.to_s] if Symbol === key }
|
72
|
+
end
|
73
|
+
|
74
|
+
def invoke
|
75
|
+
res = catch(Halt) { yield }
|
76
|
+
|
77
|
+
if Array === res
|
78
|
+
res = res.dup
|
79
|
+
|
80
|
+
if status = res.shift
|
81
|
+
response.status = status
|
82
|
+
end
|
83
|
+
|
84
|
+
if body = res.shift
|
85
|
+
response.body = body
|
86
|
+
end
|
87
|
+
else
|
88
|
+
if res
|
89
|
+
response.body = res
|
90
|
+
end
|
91
|
+
end
|
92
|
+
nil
|
93
|
+
end
|
94
|
+
|
95
|
+
def dispatch!
|
96
|
+
filter! :before
|
97
|
+
route!
|
98
|
+
rescue ::Exception => boom
|
99
|
+
gotError = true
|
100
|
+
handleException!(boom)
|
101
|
+
ensure
|
102
|
+
begin
|
103
|
+
filter! :after
|
104
|
+
rescue ::Exception => boom
|
105
|
+
handleException!(boom) unless gotError
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def handleException!(boom)
|
110
|
+
|
111
|
+
response.status = 0
|
112
|
+
|
113
|
+
errorBlock!(boom.class, boom)
|
114
|
+
|
115
|
+
raise boom
|
116
|
+
end
|
117
|
+
|
118
|
+
def halt(*response)
|
119
|
+
response = response.first if response.length == 1
|
120
|
+
throw Halt, response
|
121
|
+
end
|
122
|
+
|
123
|
+
def filter!(type)
|
124
|
+
currentClass.filters[type].each do |args|
|
125
|
+
processRoute(*args)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def route!
|
130
|
+
|
131
|
+
if block = currentClass.routes[@request.route.to_sym]
|
132
|
+
processRoute do |*args|
|
133
|
+
routeEval { block[*args] }
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
routeMissing
|
138
|
+
end
|
139
|
+
|
140
|
+
def errorBlock!(errorClass, *blockParams)
|
141
|
+
|
142
|
+
if errorBlocks = currentClass.errors[errorClass]
|
143
|
+
errorBlocks.reverse_each do |errorBlock|
|
144
|
+
args = [errorBlock]
|
145
|
+
args += [blockParams]
|
146
|
+
resp = processRoute(*args)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
if errorClass.respond_to? :superclass and errorClass.superclass <= Exception
|
151
|
+
errorBlock!(errorClass.superclass, *blockParams)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
def routeEval
|
156
|
+
throw Halt, yield
|
157
|
+
end
|
158
|
+
|
159
|
+
def processRoute(block=nil,values=[])
|
160
|
+
block ? block[self,values] : yield(self,values)
|
161
|
+
end
|
162
|
+
|
163
|
+
def routeMissing
|
164
|
+
raise NotFoundException.new("route not found")
|
165
|
+
end
|
166
|
+
|
167
|
+
def successResponse(body, args = {status: 1})
|
168
|
+
status = args[:status]
|
169
|
+
raise "status code must >= 1 when using success" if status < 1
|
170
|
+
|
171
|
+
if body.class == String
|
172
|
+
body = {msg: body}
|
173
|
+
end
|
174
|
+
|
175
|
+
response.body = body
|
176
|
+
response.status = status
|
177
|
+
nil
|
178
|
+
end
|
179
|
+
|
180
|
+
def failureResponse(body, args = {status: -1})
|
181
|
+
status = args[:status]
|
182
|
+
raise "status code must <= -1 when using failure" if status > -1
|
183
|
+
|
184
|
+
if body.class == String
|
185
|
+
body = {msg: body}
|
186
|
+
end
|
187
|
+
|
188
|
+
response.body = body
|
189
|
+
response.status = status
|
190
|
+
nil
|
191
|
+
end
|
192
|
+
|
193
|
+
def haltSuccess(body, args = {status: 1})
|
194
|
+
halt successResponse(body, args)
|
195
|
+
end
|
196
|
+
|
197
|
+
def haltFailure(body, args ={status: -1})
|
198
|
+
halt failureResponse(body, args)
|
199
|
+
end
|
200
|
+
|
201
|
+
class << self
|
202
|
+
|
203
|
+
attr_accessor :routes, :filters, :errors
|
204
|
+
|
205
|
+
def prototype
|
206
|
+
@prototype ||= new
|
207
|
+
end
|
208
|
+
|
209
|
+
def call(route, params={})
|
210
|
+
prototype.call(route, params)
|
211
|
+
end
|
212
|
+
|
213
|
+
def before(&block)
|
214
|
+
addFilter(:before, &block)
|
215
|
+
end
|
216
|
+
|
217
|
+
def after(&block)
|
218
|
+
addFilter(:after, &block)
|
219
|
+
end
|
220
|
+
|
221
|
+
def addFilter(type, &block)
|
222
|
+
@filters[type] << compile!(&block)
|
223
|
+
end
|
224
|
+
|
225
|
+
def route(symbol, &block)
|
226
|
+
block ||= Proc.new { method(symbol).call }
|
227
|
+
@routes[symbol] = compile!(&block)
|
228
|
+
end
|
229
|
+
|
230
|
+
def error(*codes, &block)
|
231
|
+
codes = codes.map { |c| Array(c) }.flatten
|
232
|
+
codes << Exception if codes.empty?
|
233
|
+
codes.each { |c| (@errors[c] ||= []) << compile!(&block) }
|
234
|
+
end
|
235
|
+
|
236
|
+
def generateUnboundMethod(&block)
|
237
|
+
methodName = :id #any symbol is ok.
|
238
|
+
define_method(methodName, &block)
|
239
|
+
method = instance_method methodName
|
240
|
+
remove_method methodName
|
241
|
+
method
|
242
|
+
end
|
243
|
+
|
244
|
+
def compile!(&block)
|
245
|
+
|
246
|
+
unboundMethod = generateUnboundMethod(&block)
|
247
|
+
|
248
|
+
block.arity != 0 ?
|
249
|
+
proc { |a,p| unboundMethod.bind(a).call(*p) } :
|
250
|
+
proc { |a,p| unboundMethod.bind(a).call }
|
251
|
+
end
|
252
|
+
|
253
|
+
def inherited(subclass)
|
254
|
+
|
255
|
+
subclass.routes = copyRoutes
|
256
|
+
subclass.filters = copyFilters
|
257
|
+
subclass.errors = copyErrors
|
258
|
+
|
259
|
+
super
|
260
|
+
end
|
261
|
+
|
262
|
+
def copyRoutes
|
263
|
+
newRoutes = {}
|
264
|
+
@routes.each do |key, value|
|
265
|
+
newRoutes[key] = value
|
266
|
+
end
|
267
|
+
newRoutes
|
268
|
+
end
|
269
|
+
|
270
|
+
def copyFilters
|
271
|
+
newFilters = {}
|
272
|
+
@filters.each do |key, values|
|
273
|
+
newValues = []
|
274
|
+
values.each do |value|
|
275
|
+
newValues << value
|
276
|
+
end
|
277
|
+
newFilters[key] = newValues
|
278
|
+
end
|
279
|
+
newFilters
|
280
|
+
end
|
281
|
+
|
282
|
+
def copyErrors
|
283
|
+
newErrors = {}
|
284
|
+
@errors.each do |key, values|
|
285
|
+
newValues = []
|
286
|
+
values.each do |value|
|
287
|
+
newValues << value
|
288
|
+
end
|
289
|
+
newErrors[key] = newValues
|
290
|
+
end
|
291
|
+
newErrors
|
292
|
+
end
|
293
|
+
|
294
|
+
def to_app
|
295
|
+
|
296
|
+
newApp = Jetra::Application.new(self)
|
297
|
+
@routes.each_key do |route|
|
298
|
+
eval("newApp.define_singleton_method(route) do |params={}| ; @app.call(route, params) ; end ")
|
299
|
+
end
|
300
|
+
|
301
|
+
eval("newApp.define_singleton_method(:method_missing) do |methodName, params={}| ; @app.call(methodName, params) ; end ")
|
302
|
+
|
303
|
+
newApp
|
304
|
+
end
|
305
|
+
|
306
|
+
end
|
307
|
+
|
308
|
+
@routes = {}
|
309
|
+
@filters = {:before => [], :after => []}
|
310
|
+
@errors = {}
|
311
|
+
|
312
|
+
error do |boom|
|
313
|
+
if boom.class == Jetra::NotFoundException
|
314
|
+
trace = []
|
315
|
+
else
|
316
|
+
trace = boom.backtrace
|
317
|
+
end
|
318
|
+
response.body = {msg: "#{boom.class} - #{boom.message}", class: boom.class.to_s, route: request.route, params: params, trace: trace}
|
319
|
+
halt
|
320
|
+
end
|
321
|
+
|
322
|
+
end
|
323
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require "jetra/application"
|
2
|
+
|
3
|
+
module Jetra
|
4
|
+
|
5
|
+
class Builder
|
6
|
+
|
7
|
+
def initialize(&block)
|
8
|
+
@use = []
|
9
|
+
instance_eval(&block) if block_given?
|
10
|
+
|
11
|
+
@app = build_app
|
12
|
+
end
|
13
|
+
|
14
|
+
def call(route, params={})
|
15
|
+
@app.call(route, params)
|
16
|
+
end
|
17
|
+
|
18
|
+
def routes
|
19
|
+
@run.routes
|
20
|
+
end
|
21
|
+
|
22
|
+
def to_app
|
23
|
+
|
24
|
+
newApp = Jetra::Application.new(self)
|
25
|
+
routes.each_key do |route|
|
26
|
+
eval("newApp.define_singleton_method(route) do |params={}| ; @app.call(route, params) ; end ")
|
27
|
+
end
|
28
|
+
|
29
|
+
eval("newApp.define_singleton_method(:method_missing) do |methodName, params={}| ; @app.call(methodName, params) ; end ")
|
30
|
+
|
31
|
+
newApp
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def use(middleware)
|
37
|
+
@use << proc { |app| middleware.new(app) }
|
38
|
+
end
|
39
|
+
|
40
|
+
def run(app)
|
41
|
+
@run = app
|
42
|
+
end
|
43
|
+
|
44
|
+
def build_app
|
45
|
+
app = @run
|
46
|
+
fail "missing run statement" unless app
|
47
|
+
app = @use.reverse.inject(app) { |a,e| e[a] }
|
48
|
+
app
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require "jetra/application"
|
2
|
+
|
3
|
+
module Jetra
|
4
|
+
|
5
|
+
class Combiner
|
6
|
+
|
7
|
+
def initialize(&block)
|
8
|
+
|
9
|
+
@leader = nil
|
10
|
+
@routes = {}
|
11
|
+
|
12
|
+
instance_eval(&block) if block_given?
|
13
|
+
|
14
|
+
fail "missing mount statement" unless @leader
|
15
|
+
end
|
16
|
+
|
17
|
+
def call(route, params={})
|
18
|
+
if app = @routes[route]
|
19
|
+
app.call(route, params)
|
20
|
+
else
|
21
|
+
@leader.call(route, params)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def routes
|
26
|
+
@routes
|
27
|
+
end
|
28
|
+
|
29
|
+
def to_app
|
30
|
+
|
31
|
+
newApp = Jetra::Application.new(self)
|
32
|
+
routes.each_key do |route|
|
33
|
+
eval("newApp.define_singleton_method(route) do |params={}| ; @app.call(route, params) ; end ")
|
34
|
+
end
|
35
|
+
|
36
|
+
eval("newApp.define_singleton_method(:method_missing) do |method_name, params={}| ; @app.call(method_name, params) ; end ")
|
37
|
+
|
38
|
+
newApp
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def mount(app)
|
44
|
+
|
45
|
+
app.routes.each_key do |route|
|
46
|
+
@routes[route] ||= app
|
47
|
+
end
|
48
|
+
|
49
|
+
@leader ||= app
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Jetra
|
2
|
+
|
3
|
+
module Middleware
|
4
|
+
|
5
|
+
#简单的中间件示例
|
6
|
+
class Sample
|
7
|
+
|
8
|
+
def initialize(app)
|
9
|
+
@app = app
|
10
|
+
end
|
11
|
+
|
12
|
+
def call(route, params)
|
13
|
+
|
14
|
+
#you can do something you like before call
|
15
|
+
|
16
|
+
#puts "sample middleware call start.."
|
17
|
+
|
18
|
+
response = @app.call(route, params)
|
19
|
+
|
20
|
+
#puts "sample middleware call finish.."
|
21
|
+
|
22
|
+
#you can do something you like after call
|
23
|
+
|
24
|
+
response
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Jetra
|
2
|
+
|
3
|
+
module Middleware
|
4
|
+
|
5
|
+
#验证参数和返回值的基本类型
|
6
|
+
class Validater
|
7
|
+
|
8
|
+
def initialize(app)
|
9
|
+
@app = app
|
10
|
+
end
|
11
|
+
|
12
|
+
def call(route, params)
|
13
|
+
|
14
|
+
if !params.kind_of?(Hash)
|
15
|
+
response = Jetra::Response.new
|
16
|
+
response.status = 0
|
17
|
+
response.body = {msg: "Jetra::Middleware::Validater: params type miss match. excepted Hash, got #{params.class.to_s}"}
|
18
|
+
else
|
19
|
+
response = @app.call(route, params)
|
20
|
+
if !response.status.kind_of?(Integer)
|
21
|
+
response.status = 0
|
22
|
+
response.body = {msg: "Jetra::Middleware::Validater: response.status type miss match. excepted Integer, got #{response.status.class.to_s}"}
|
23
|
+
else
|
24
|
+
if !response.body.kind_of?(Hash)
|
25
|
+
response.status = 0
|
26
|
+
response.body = {msg: "Jetra::Middleware::Validater: response.body type miss match. excepted Hash, got #{response.body.class.to_s}"}
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
response
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
metadata
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: jetra
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Jeffrey
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-04-20 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: micro DSL
|
14
|
+
email:
|
15
|
+
- jeffrey6052@163.com
|
16
|
+
executables: []
|
17
|
+
extensions: []
|
18
|
+
extra_rdoc_files: []
|
19
|
+
files:
|
20
|
+
- lib/jetra.rb
|
21
|
+
- lib/jetra/adapter/rack.rb
|
22
|
+
- lib/jetra/application.rb
|
23
|
+
- lib/jetra/base.rb
|
24
|
+
- lib/jetra/builder.rb
|
25
|
+
- lib/jetra/combiner.rb
|
26
|
+
- lib/jetra/middleware/sample.rb
|
27
|
+
- lib/jetra/middleware/validater.rb
|
28
|
+
- lib/jetra/version.rb
|
29
|
+
homepage:
|
30
|
+
licenses:
|
31
|
+
- MIT
|
32
|
+
metadata: {}
|
33
|
+
post_install_message:
|
34
|
+
rdoc_options: []
|
35
|
+
require_paths:
|
36
|
+
- lib
|
37
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - ">="
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '0'
|
42
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '0'
|
47
|
+
requirements: []
|
48
|
+
rubyforge_project:
|
49
|
+
rubygems_version: 2.6.8
|
50
|
+
signing_key:
|
51
|
+
specification_version: 4
|
52
|
+
summary: make it easy to write micro service
|
53
|
+
test_files: []
|