plezi 0.11.2 → 0.12.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 +4 -4
- data/CHANGELOG.md +20 -0
- data/README.md +32 -33
- data/docs/async_helpers.md +90 -40
- data/docs/logging.md +38 -3
- data/docs/routes.md +177 -5
- data/docs/websockets.md +5 -0
- data/lib/plezi.rb +2 -10
- data/lib/plezi/common/api.rb +48 -101
- data/lib/plezi/common/defer.rb +4 -4
- data/lib/plezi/common/dsl.rb +13 -24
- data/lib/plezi/common/redis.rb +9 -5
- data/lib/plezi/common/settings.rb +4 -24
- data/lib/plezi/handlers/controller_core.rb +7 -14
- data/lib/plezi/handlers/controller_magic.rb +12 -10
- data/lib/plezi/handlers/http_router.rb +27 -22
- data/lib/plezi/handlers/placebo.rb +40 -33
- data/lib/plezi/handlers/route.rb +233 -234
- data/lib/plezi/handlers/session.rb +2 -2
- data/lib/plezi/handlers/stubs.rb +7 -0
- data/lib/plezi/handlers/ws_object.rb +25 -15
- data/lib/plezi/helpers/http_sender.rb +3 -3
- data/lib/plezi/helpers/magic_helpers.rb +3 -3
- data/lib/plezi/version.rb +1 -1
- data/plezi.gemspec +1 -1
- data/resources/config.ru +12 -18
- data/resources/environment.rb +6 -7
- data/resources/mini_app.rb +22 -22
- data/resources/redis_config.rb +4 -2
- data/test/plezi_tests.rb +76 -87
- data/websocket chatroom.md +3 -5
- metadata +6 -6
- data/docs/http_helpers.md +0 -9
data/lib/plezi/handlers/route.rb
CHANGED
@@ -1,271 +1,270 @@
|
|
1
1
|
module Plezi
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
# the parameters for the router and service that were used to create the service, router and host.
|
13
|
-
attr_reader :params
|
2
|
+
module Base
|
3
|
+
class Route
|
4
|
+
# the Regexp that will be used to match the request.
|
5
|
+
attr_reader :path
|
6
|
+
# the controller that answers the request on this path (if exists).
|
7
|
+
attr_reader :controller
|
8
|
+
# the proc that answers the request on this path (if exists).
|
9
|
+
attr_reader :proc
|
10
|
+
# the parameters for the router and service that were used to create the service, router and host.
|
11
|
+
attr_reader :params
|
14
12
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
13
|
+
# lets the route answer the request. returns false if no response has been sent.
|
14
|
+
def on_request request, response
|
15
|
+
fill_parameters = match request.path
|
16
|
+
return false unless fill_parameters
|
17
|
+
old_params = request.params.dup
|
18
|
+
fill_parameters.each {|k,v| Plezi::Base::Helpers.add_param_to_hash k, v, request.params }
|
19
|
+
ret = false
|
20
|
+
if controller
|
21
|
+
ret = controller.new(request, response)._route_path_to_methods_and_set_the_response_
|
22
|
+
elsif proc
|
23
|
+
ret = proc.call(request, response)
|
24
|
+
elsif controller == false
|
25
|
+
request.path = path.match(request.path).to_a.last.to_s
|
26
|
+
return false
|
27
|
+
end
|
28
|
+
unless ret
|
29
|
+
request.params.replace old_params unless fill_parameters.empty?
|
30
|
+
return false
|
31
|
+
end
|
32
|
+
return ret
|
33
33
|
end
|
34
|
-
return ret
|
35
|
-
end
|
36
34
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
35
|
+
# the initialize method accepts a Regexp or a String and creates the path object.
|
36
|
+
#
|
37
|
+
# Regexp paths will be left unchanged
|
38
|
+
#
|
39
|
+
# a string can be either a simple string `"/users"` or a string with parameters:
|
40
|
+
# `"/static/:required/(:optional)/(:optional_with_format){[\d]*}/:optional_2"`
|
41
|
+
def initialize path, controller, params={}, &block
|
42
|
+
@original_path, @url_array, @params = path, false, params
|
43
|
+
initialize_path( (controller == false) ? "#{path.chomp('/')}/*" : path )
|
44
|
+
initialize_controller controller, block
|
45
|
+
end
|
48
46
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
47
|
+
# initializes the controller,
|
48
|
+
# by inheriting the class into an Plezi controller subclass (with the Plezi::ControllerMagic injected).
|
49
|
+
#
|
50
|
+
# Proc objects are currently passed through without any change - as Proc routes are assumed to handle themselves correctly.
|
51
|
+
def initialize_controller controller, block
|
52
|
+
@controller, @proc = controller, block
|
53
|
+
if controller.is_a?(Class)
|
54
|
+
# add controller magic
|
55
|
+
@controller = self.class.make_controller_magic controller, self
|
56
|
+
end
|
58
57
|
end
|
59
|
-
end
|
60
58
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
59
|
+
# # returns the url for THIS route (i.e. `url_for :index`)
|
60
|
+
# #
|
61
|
+
# # This will be usually used by the Controller's #url_for method to get the relative part of the url.
|
62
|
+
# def url_for dest = :index
|
63
|
+
# raise NotImplementedError, "#url_for isn't implemented for this router - could this be a Regexp based router?" unless @url_array
|
64
|
+
# # convert dest.id and dest[:id] to their actual :id value.
|
65
|
+
# dest = (dest.id rescue false) || (raise TypeError, "Expecting a Symbol, Hash, String, Numeric or an object that answers to obj[:id] or obj.id") unless !dest || dest.is_a?(Symbol) || dest.is_a?(String) || dest.is_a?(Numeric) || dest.is_a?(Hash)
|
66
|
+
# url = '/'
|
67
|
+
# case dest
|
68
|
+
# when false, nil, '', :index
|
69
|
+
# add = true
|
70
|
+
# @url_array.each do |sec|
|
71
|
+
# add = false unless sec[0] != :path
|
72
|
+
# url << sec[1] if add
|
73
|
+
# raise NotImplementedError, '#url_for(index) cannot be implementedfor this path.' if !add && sec[0] == :path
|
74
|
+
# # todo: :multi_path
|
75
|
+
# end
|
76
|
+
# when Hash
|
77
|
+
# when Symbol, String, Numeric
|
78
|
+
# end
|
79
|
+
# end
|
82
80
|
|
83
81
|
|
84
82
|
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
83
|
+
# returns the url for THIS route (i.e. `url_for :index`)
|
84
|
+
#
|
85
|
+
# This will be usually used by the Controller's #url_for method to get the relative part of the url.
|
86
|
+
def url_for dest = :index
|
87
|
+
raise NotImplementedError, "#url_for isn't implemented for this router - could this be a Regexp based router?" unless @url_array
|
88
|
+
case dest
|
89
|
+
when String
|
90
|
+
dest = {id: dest.dup}
|
91
|
+
when Numeric, Symbol
|
92
|
+
dest = {id: dest}
|
93
|
+
when Hash
|
94
|
+
dest = dest.dup
|
95
|
+
dest.each {|k,v| dest[k] = v.dup if v.is_a? String }
|
96
|
+
when nil, false
|
97
|
+
dest = {}
|
98
|
+
else
|
99
|
+
# convert dest.id and dest[:id] to their actual :id value.
|
100
|
+
dest = {id: (dest.id rescue false) || (raise TypeError, "Expecting a Symbol, Hash, String, Numeric or an object that answers to obj[:id] or obj.id") }
|
101
|
+
end
|
102
|
+
dest.default_proc = Plezi::Base::Helpers::HASH_SYM_PROC
|
105
103
|
|
106
|
-
|
104
|
+
url = '/'
|
107
105
|
|
108
|
-
|
109
|
-
|
106
|
+
@url_array.each do |sec|
|
107
|
+
raise NotImplementedError, "#url_for isn't implemented for this router - Regexp multi-path routes are still being worked on... use a named parameter instead (i.e. '/foo/(:multi_route){route1|route2}/bar')" if REGEXP_FORMATTED_PATH === sec
|
110
108
|
|
111
|
-
|
112
|
-
|
109
|
+
param_name = (REGEXP_OPTIONAL_PARAMS.match(sec) || REGEXP_FORMATTED_OPTIONAL_PARAMS.match(sec) || REGEXP_REQUIRED_PARAMS.match(sec) || REGEXP_FORMATTED_REQUIRED_PARAMS.match(sec))
|
110
|
+
param_name = param_name[1].to_sym if param_name
|
113
111
|
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
112
|
+
if param_name && dest[param_name]
|
113
|
+
url << Plezi::Base::Helpers.encode_url(dest.delete(param_name))
|
114
|
+
url << '/'
|
115
|
+
elsif !param_name
|
116
|
+
url << sec
|
117
|
+
url << '/'
|
118
|
+
elsif REGEXP_REQUIRED_PARAMS === sec || REGEXP_OPTIONAL_PARAMS === sec
|
119
|
+
url << '/'
|
120
|
+
elsif REGEXP_FORMATTED_REQUIRED_PARAMS === sec
|
121
|
+
raise ArgumentError, "URL can't be formatted becuse a required parameter (#{param_name.to_s}) isn't specified and it requires a special format (#{REGEXP_FORMATTED_REQUIRED_PARAMS.match(sec)[2]})."
|
122
|
+
end
|
124
123
|
end
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
url
|
124
|
+
unless dest.empty?
|
125
|
+
add = '?'
|
126
|
+
dest.each {|k, v| url << "#{add}#{Plezi::Base::Helpers.encode_url k}=#{Plezi::Base::Helpers.encode_url v}"; add = '&'}
|
127
|
+
end
|
128
|
+
url
|
131
129
|
|
132
|
-
|
130
|
+
end
|
133
131
|
|
134
132
|
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
133
|
+
# Used to check for routes formatted: /:paramater - required parameters
|
134
|
+
REGEXP_REQUIRED_PARAMS = /^\:([^\(\)\{\}\:]*)$/
|
135
|
+
# Used to check for routes formatted: /(:paramater) - optional parameters
|
136
|
+
REGEXP_OPTIONAL_PARAMS = /^\(\:([^\(\)\{\}\:]*)\)$/
|
137
|
+
# Used to check for routes formatted: /(:paramater){regexp} - optional formatted parameters
|
138
|
+
REGEXP_FORMATTED_OPTIONAL_PARAMS = /^\(\:([^\(\)\{\}\:]*)\)\{(.*)\}$/
|
139
|
+
# Used to check for routes formatted: /:paramater{regexp} - required parameters
|
140
|
+
REGEXP_FORMATTED_REQUIRED_PARAMS = /^\:([^\(\)\{\}\:\/]*)\{(.*)\}$/
|
141
|
+
# Used to check for routes formatted: /{regexp} - required path
|
142
|
+
REGEXP_FORMATTED_PATH = /^\{(.*)\}$/
|
145
143
|
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
144
|
+
# initializes the path by converting the string into a Regexp
|
145
|
+
# and noting any parameters that might need to be extracted for RESTful routes.
|
146
|
+
def initialize_path path
|
147
|
+
@fill_parameters = {}
|
148
|
+
if path.is_a? Regexp
|
149
|
+
@path = path
|
150
|
+
elsif path.is_a? String
|
151
|
+
# prep used prameters
|
152
|
+
param_num = 0
|
155
153
|
|
156
|
-
|
157
|
-
|
154
|
+
section_search = "([\\/][^\\/]*)"
|
155
|
+
optional_section_search = "([\\/][^\\/]*)?"
|
158
156
|
|
159
|
-
|
160
|
-
|
157
|
+
@path = '^'
|
158
|
+
@url_array = []
|
161
159
|
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
160
|
+
# prep path string and split it where the '/' charected is unescaped.
|
161
|
+
@url_array = path.gsub(/(^\/)|(\/$)/, '').gsub(/([^\\])\//, '\1 - /').split ' - /'
|
162
|
+
@url_array.each.with_index do |section, section_index|
|
163
|
+
if section == '*'
|
164
|
+
# create catch all
|
165
|
+
section_index == 0 ? (@path << "(.*)") : (@path << "(\\/.*)?")
|
166
|
+
# finish
|
167
|
+
@path = /#{@path}$/
|
168
|
+
return
|
171
169
|
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
170
|
+
# check for routes formatted: /:paramater - required parameters
|
171
|
+
elsif section.match REGEXP_REQUIRED_PARAMS
|
172
|
+
#create a simple section catcher
|
173
|
+
@path << section_search
|
174
|
+
# add paramater recognition value
|
175
|
+
@fill_parameters[param_num += 1] = section.match(REGEXP_REQUIRED_PARAMS)[1]
|
178
176
|
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
177
|
+
# check for routes formatted: /(:paramater) - optional parameters
|
178
|
+
elsif section.match REGEXP_OPTIONAL_PARAMS
|
179
|
+
#create a optional section catcher
|
180
|
+
@path << optional_section_search
|
181
|
+
# add paramater recognition value
|
182
|
+
@fill_parameters[param_num += 1] = section.match(REGEXP_OPTIONAL_PARAMS)[1]
|
185
183
|
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
184
|
+
# check for routes formatted: /(:paramater){regexp} - optional parameters
|
185
|
+
elsif section.match REGEXP_FORMATTED_OPTIONAL_PARAMS
|
186
|
+
#create a optional section catcher
|
187
|
+
@path << ( "(\/(" + section.match(REGEXP_FORMATTED_OPTIONAL_PARAMS)[2] + "))?" )
|
188
|
+
# add paramater recognition value
|
189
|
+
@fill_parameters[param_num += 1] = section.match(REGEXP_FORMATTED_OPTIONAL_PARAMS)[1]
|
190
|
+
param_num += 1 # we are using two spaces - param_num += should look for () in regex ? /[^\\](/
|
193
191
|
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
192
|
+
# check for routes formatted: /:paramater{regexp} - required parameters
|
193
|
+
elsif section.match REGEXP_FORMATTED_REQUIRED_PARAMS
|
194
|
+
#create a simple section catcher
|
195
|
+
@path << ( "(\/(" + section.match(REGEXP_FORMATTED_REQUIRED_PARAMS)[2] + "))" )
|
196
|
+
# add paramater recognition value
|
197
|
+
@fill_parameters[param_num += 1] = section.match(REGEXP_FORMATTED_REQUIRED_PARAMS)[1]
|
198
|
+
param_num += 1 # we are using two spaces - param_num += should look for () in regex ? /[^\\](/
|
201
199
|
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
200
|
+
# check for routes formatted: /{regexp} - formated path
|
201
|
+
elsif section.match REGEXP_FORMATTED_PATH
|
202
|
+
#create a simple section catcher
|
203
|
+
@path << ( "\/(" + section.match(REGEXP_FORMATTED_PATH)[1] + ")" )
|
204
|
+
# add paramater recognition value
|
205
|
+
param_num += 1 # we are using one space - param_num += should look for () in regex ? /[^\\](/
|
206
|
+
else
|
207
|
+
@path << "\/"
|
208
|
+
@path << section
|
209
|
+
end
|
211
210
|
end
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
end
|
211
|
+
unless @fill_parameters.values.include?("id")
|
212
|
+
@path << optional_section_search
|
213
|
+
@fill_parameters[param_num += 1] = "id"
|
214
|
+
@url_array << '(:id)'
|
215
|
+
end
|
216
|
+
# set the Regexp and return the final result.
|
217
|
+
@path = /#{@path}$/
|
218
|
+
else
|
219
|
+
raise "Path cannot be initialized - path must be either a string or a regular experssion."
|
220
|
+
end
|
221
|
+
return
|
222
|
+
end
|
225
223
|
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
224
|
+
# this performs the match and assigns the parameters, if required.
|
225
|
+
def match path
|
226
|
+
hash = {}
|
227
|
+
# m = nil
|
228
|
+
# unless @fill_parameters.values.include?("format")
|
229
|
+
# if (m = path.match /([^\.]*)\.([^\.\/]+)$/)
|
230
|
+
# Plezi::Base::Helpers.add_param_to_hash 'format', m[2], hash
|
231
|
+
# path = m[1]
|
232
|
+
# end
|
233
|
+
# end
|
234
|
+
m = @path.match path
|
235
|
+
return false unless m
|
236
|
+
@fill_parameters.each { |k, v| hash[v] = m[k][1..-1] if m[k] && m[k] != '/' }
|
237
|
+
hash
|
238
|
+
end
|
241
239
|
|
242
|
-
|
243
|
-
|
240
|
+
###########
|
241
|
+
## class magic methods
|
244
242
|
|
245
|
-
|
243
|
+
protected
|
246
244
|
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
245
|
+
# injects some magic to the controller
|
246
|
+
#
|
247
|
+
# adds the `redirect_to` and `send_data` methods to the controller class, as well as the properties:
|
248
|
+
# env:: the env recieved by the Rack server.
|
249
|
+
# params:: the request's parameters.
|
250
|
+
# cookies:: the request's cookies.
|
251
|
+
# flash:: an amazing Hash object that sets temporary cookies for one request only - greate for saving data between redirect calls.
|
252
|
+
#
|
253
|
+
def self.make_controller_magic(controller, container)
|
254
|
+
new_class_name = "Plezi__#{controller.name.gsub /[\:\-\#\<\>\{\}\(\)\s]/, '_'}"
|
255
|
+
return Module.const_get new_class_name if Module.const_defined? new_class_name
|
256
|
+
# controller.include Plezi::ControllerMagic
|
257
|
+
controller.instance_exec(container) {|r| include Plezi::ControllerMagic; }
|
258
|
+
ret = Class.new(controller) do
|
259
|
+
include Plezi::Base::ControllerCore
|
260
|
+
end
|
261
|
+
Object.const_set(new_class_name, ret)
|
262
|
+
Module.const_get(new_class_name).reset_routing_cache
|
263
|
+
ret.instance_exec(container) {|r| set_pl_route r;}
|
264
|
+
ret
|
262
265
|
end
|
263
|
-
Object.const_set(new_class_name, ret)
|
264
|
-
Module.const_get(new_class_name).reset_routing_cache
|
265
|
-
ret.instance_exec(container) {|r| set_pl_route r;}
|
266
|
-
ret
|
267
|
-
end
|
268
266
|
|
267
|
+
end
|
269
268
|
end
|
270
269
|
|
271
270
|
end
|