plezi 0.14.4 → 0.14.5
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 +12 -0
- data/bin/ws_shootout +30 -0
- data/exe/plezi +108 -108
- data/lib/plezi.rb +18 -26
- data/lib/plezi/activation.rb +16 -16
- data/lib/plezi/api.rb +51 -50
- data/lib/plezi/controller/controller.rb +249 -229
- data/lib/plezi/controller/controller_class.rb +189 -174
- data/lib/plezi/controller/cookies.rb +49 -47
- data/lib/plezi/helpers.rb +35 -35
- data/lib/plezi/render/erb.rb +25 -26
- data/lib/plezi/render/has_cache.rb +31 -31
- data/lib/plezi/render/markdown.rb +53 -53
- data/lib/plezi/render/render.rb +36 -38
- data/lib/plezi/render/sass.rb +43 -44
- data/lib/plezi/render/slim.rb +25 -25
- data/lib/plezi/router/adclient.rb +14 -15
- data/lib/plezi/router/assets.rb +59 -61
- data/lib/plezi/router/errors.rb +22 -22
- data/lib/plezi/router/route.rb +98 -100
- data/lib/plezi/router/router.rb +120 -113
- data/lib/plezi/version.rb +1 -1
- data/lib/plezi/websockets/message_dispatch.rb +118 -80
- data/lib/plezi/websockets/redis.rb +42 -43
- data/plezi.gemspec +3 -3
- data/resources/client.js +229 -204
- data/resources/ctrlr.rb +26 -26
- data/resources/mini_app.rb +1 -1
- data/resources/simple-client.js +50 -43
- metadata +9 -8
@@ -1,176 +1,191 @@
|
|
1
|
-
require 'json'
|
2
1
|
module Plezi
|
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
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
2
|
+
module Controller
|
3
|
+
# this module extends the controller class with Plezi functions
|
4
|
+
module ClassMethods
|
5
|
+
# A Ruby callback used to initialize class data for new Controllers.
|
6
|
+
def self.extended(base)
|
7
|
+
base._pl_init_class_data
|
8
|
+
end
|
9
|
+
|
10
|
+
# Returns a relative URL for the controller, placing the requested parameters in the URL (inline, where possible and as query data when not possible).
|
11
|
+
def url_for(func, params = {})
|
12
|
+
::Plezi::Base::Router.url_for self, func, params
|
13
|
+
end
|
14
|
+
|
15
|
+
# Invokes a method on the `target` websocket connection. When using Iodine, the method is invoked asynchronously.
|
16
|
+
#
|
17
|
+
# self.unicast target, :my_method, "argument 1"
|
18
|
+
#
|
19
|
+
# Methods invoked using {unicast}, {broadcast} or {multicast} will quitely fail if the connection was lost, the requested method is undefined or the 'target' was invalid.
|
20
|
+
def unicast(target, event_method, *args)
|
21
|
+
::Plezi::Base::MessageDispatch.unicast(self, target, event_method, args)
|
22
|
+
end
|
23
|
+
|
24
|
+
# Invokes a method on every websocket connection that belongs to this Controller / Type. When using Iodine, the method is invoked asynchronously.
|
25
|
+
#
|
26
|
+
# self.broadcast :my_method, "argument 1", "argument 2", 3
|
27
|
+
#
|
28
|
+
# Methods invoked using {unicast}, {broadcast} or {multicast} will quitely fail if the connection was lost, the requested method is undefined or the 'target' was invalid.
|
29
|
+
def broadcast(event_method, *args)
|
30
|
+
::Plezi::Base::MessageDispatch.broadcast(self, event_method, args)
|
31
|
+
end
|
32
|
+
|
33
|
+
# Invokes a method on every websocket connection in the application.
|
34
|
+
#
|
35
|
+
# self.multicast :my_method, "argument 1", "argument 2", 3
|
36
|
+
#
|
37
|
+
# Methods invoked using {unicast}, {broadcast} or {multicast} will quitely fail if the connection was lost, the requested method is undefined or the 'target' was invalid.
|
38
|
+
def multicast(event_method, *args)
|
39
|
+
::Plezi::Base::MessageDispatch.multicast(self, event_method, args)
|
40
|
+
end
|
41
|
+
# Writes a message to every client websocket connection in all controllers(!). Accepts an optional filter method using a location reference for a *static* (Class/Module/global) method. The filter method will be passerd the websocket object and it should return `true` / `false`.
|
42
|
+
#
|
43
|
+
# self.write2everyone {event: "global", message: "This will be sent to everyone"}.to_json
|
44
|
+
# # or, we can define a filter method somewhere in our code
|
45
|
+
# module Filter
|
46
|
+
# def self.should_send? ws
|
47
|
+
# true
|
48
|
+
# end
|
49
|
+
# end
|
50
|
+
# # and we can use this filter method.
|
51
|
+
# data = {event: "global", message: "This will be sent to everyone"}.to_json
|
52
|
+
# self.write2everyone data, ::Filter, :should_send?
|
53
|
+
#
|
54
|
+
# It's important that the filter method is defined statically in our code and isn't dynamically allocated. Otherwise, scaling the application would be impossible.
|
55
|
+
def write2everyone(data, filter_owner = nil, filter_name = nil)
|
56
|
+
::Plezi::Base::MessageDispatch.write2everyone(self, data, filter_owner, filter_name)
|
57
|
+
end
|
58
|
+
|
59
|
+
# @private
|
60
|
+
# This is used internally by Plezi, do not use.
|
61
|
+
RESERVED_METHODS = [:delete, :create, :update, :new, :show, :pre_connect, :on_open, :on_close, :on_shutdown, :on_message].freeze
|
62
|
+
# @private
|
63
|
+
# This function is used internally by Plezi, do not call.
|
64
|
+
def _pl_get_map
|
65
|
+
return @_pl_get_map if @_pl_get_map
|
66
|
+
|
67
|
+
@_pl_get_map = {}
|
68
|
+
mths = public_instance_methods false
|
69
|
+
mths.delete_if { |mthd| mthd.to_s[0] == '_' || !(-1..0).cover?(instance_method(mthd).arity) }
|
70
|
+
@_pl_get_map[nil] = :index if mths.include?(:index)
|
71
|
+
RESERVED_METHODS.each { |mthd| mths.delete mthd }
|
72
|
+
mths.each { |mthd| @_pl_get_map[mthd.to_s.freeze] = mthd }
|
73
|
+
|
74
|
+
@_pl_get_map
|
75
|
+
end
|
76
|
+
|
77
|
+
# @private
|
78
|
+
# This function is used internally by Plezi, do not call.
|
79
|
+
def _pl_has_delete
|
80
|
+
@_pl_has_delete
|
81
|
+
end
|
82
|
+
|
83
|
+
# @private
|
84
|
+
# This function is used internally by Plezi, do not call.
|
85
|
+
def _pl_has_update
|
86
|
+
@_pl_has_update
|
87
|
+
end
|
88
|
+
|
89
|
+
# @private
|
90
|
+
# This function is used internally by Plezi, do not call.
|
91
|
+
def _pl_has_create
|
92
|
+
@_pl_has_create
|
93
|
+
end
|
94
|
+
|
95
|
+
# @private
|
96
|
+
# This function is used internally by Plezi, do not call.
|
97
|
+
def _pl_has_new
|
98
|
+
@_pl_has_new
|
99
|
+
end
|
100
|
+
|
101
|
+
# @private
|
102
|
+
# This function is used internally by Plezi, do not call.
|
103
|
+
def _pl_has_show
|
104
|
+
@_pl_has_show
|
105
|
+
end
|
106
|
+
|
107
|
+
# @private
|
108
|
+
# This function is used internally by Plezi, do not call.
|
109
|
+
def _pl_is_websocket?
|
110
|
+
@_pl_is_websocket
|
111
|
+
end
|
112
|
+
|
113
|
+
# @private
|
114
|
+
# This function is used internally by Plezi, do not call.
|
115
|
+
def _pl_is_ad?
|
116
|
+
@auto_dispatch
|
117
|
+
end
|
118
|
+
|
119
|
+
# @private
|
120
|
+
# This function is used internally by Plezi, do not call.
|
121
|
+
def _pl_ws_map
|
122
|
+
return @_pl_ws_map if @_pl_ws_map
|
123
|
+
|
124
|
+
@_pl_ws_map = {}
|
125
|
+
mths = instance_methods false
|
126
|
+
mths.delete :index
|
127
|
+
RESERVED_METHODS.each { |mthd| mths.delete mthd }
|
128
|
+
mths.each { |mthd| @_pl_ws_map[mthd.to_s.freeze] = mthd; @_pl_ws_map[mthd] = mthd }
|
129
|
+
|
130
|
+
@_pl_ws_map
|
131
|
+
end
|
132
|
+
|
133
|
+
# @private
|
134
|
+
# This function is used internally by Plezi, do not call.
|
135
|
+
def _pl_ad_map
|
136
|
+
return @_pl_ad_map if @_pl_ad_map
|
137
|
+
|
138
|
+
@_pl_ad_map = {}
|
139
|
+
mths = public_instance_methods false
|
140
|
+
mths.delete_if { |m| m.to_s[0] == '_' || ![-2, -1, 1].freeze.include?(instance_method(m).arity) }
|
141
|
+
mths.delete :index
|
142
|
+
RESERVED_METHODS.each { |m| mths.delete m }
|
143
|
+
mths.each { |m| @_pl_ad_map[m.to_s.freeze] = m; @_pl_ad_map[m] = m }
|
144
|
+
|
145
|
+
@_pl_ad_map
|
146
|
+
end
|
147
|
+
|
148
|
+
# @private
|
149
|
+
# This function is used internally by Plezi, do not call.
|
150
|
+
def _pl_params2method(params, env)
|
151
|
+
par_id = params['id'.freeze]
|
152
|
+
meth_id = _pl_get_map[par_id]
|
153
|
+
return meth_id if par_id && meth_id
|
154
|
+
# puts "matching against #{params}"
|
155
|
+
case params['_method'.freeze]
|
156
|
+
when :get # since this is common, it's pushed upwards.
|
157
|
+
if env['HTTP_UPGRADE'.freeze] && _pl_is_websocket? && env['HTTP_UPGRADE'.freeze].downcase.start_with?('websocket'.freeze)
|
158
|
+
@_pl_init_global_data ||= ::Plezi.plezi_initialize # wake up pub/sub drivers in case of `fork`
|
159
|
+
return :preform_upgrade
|
160
|
+
end
|
161
|
+
return :new if _pl_has_new && par_id == 'new'.freeze
|
162
|
+
return meth_id || (_pl_has_show && :show) || nil
|
163
|
+
when :put, :patch
|
164
|
+
return :create if _pl_has_create && (par_id.nil? || par_id == 'new'.freeze)
|
165
|
+
return :update if _pl_has_update
|
166
|
+
when :post
|
167
|
+
return :create if _pl_has_create
|
168
|
+
when :delete
|
169
|
+
return :delete if _pl_has_delete
|
170
|
+
end
|
171
|
+
meth_id || (_pl_has_show && :show) || nil
|
172
|
+
end
|
173
|
+
|
174
|
+
# @private
|
175
|
+
# This function is used internally by Plezi, do not call.
|
176
|
+
def _pl_init_class_data
|
177
|
+
@auto_dispatch ||= nil
|
178
|
+
@_pl_get_map = @_pl_ad_map = @_pl_ws_map = nil
|
179
|
+
@_pl_has_show = public_instance_methods(false).include?(:show)
|
180
|
+
@_pl_has_new = public_instance_methods(false).include?(:new)
|
181
|
+
@_pl_has_create = public_instance_methods(false).include?(:create)
|
182
|
+
@_pl_has_update = public_instance_methods(false).include?(:update)
|
183
|
+
@_pl_has_delete = public_instance_methods(false).include?(:delete)
|
184
|
+
@_pl_is_websocket = (instance_variable_defined?(:@auto_dispatch) && instance_variable_get(:@auto_dispatch)) || instance_methods(false).include?(:on_message)
|
185
|
+
_pl_get_map
|
186
|
+
_pl_ad_map
|
187
|
+
_pl_ws_map
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
176
191
|
end
|
@@ -1,54 +1,56 @@
|
|
1
1
|
module Plezi
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
2
|
+
module Controller
|
3
|
+
# The cookie jar class. Controllers have an instance of this class named `cookies`.
|
4
|
+
class Cookies < Hash
|
5
|
+
attr_reader :request, :response
|
6
|
+
def initialize(request, response)
|
7
|
+
@request = request
|
8
|
+
@response = response
|
9
|
+
end
|
10
|
+
|
11
|
+
# Reads a cookie from either the request cookie Hash or the new cookies Hash.
|
12
|
+
def[](key)
|
13
|
+
if key.is_a? Symbol
|
14
|
+
super(key) || super(key.to_s) || @request.cookies[key] || @request.cookies[key.to_s]
|
15
|
+
elsif key.is_a? String
|
16
|
+
super(key) || super(key.to_sym) || @request.cookies[key] || @request.cookies[key.to_sym]
|
17
|
+
else
|
18
|
+
super(key) || @request.cookies[key]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# Sets (or deletes) a cookie. New cookies are placed in the new cookie Hash and are accessible only to the controller that created them.
|
23
|
+
def[]=(key, value)
|
24
|
+
if value.nil?
|
25
|
+
@response.delete_cookie key
|
26
|
+
delete key
|
27
|
+
if key.is_a? Symbol
|
28
|
+
delete key.to_s
|
29
|
+
elsif key.is_a? String
|
30
|
+
delete key.to_sym
|
31
|
+
end
|
32
|
+
return nil
|
33
|
+
end
|
34
|
+
@response.set_cookie key, value
|
35
|
+
value = value[:value] if value.is_a? Hash
|
36
|
+
super
|
37
|
+
end
|
38
|
+
end
|
39
|
+
# Writes a line dlimited string of all the existing and the new cookies. i.e.:
|
40
|
+
# name1=value1
|
41
|
+
# name2=value2
|
42
|
+
def to_s
|
43
|
+
(@request ? (to_a + request.cookies.to_a) : to_a).map! { |pair| pair.join('=') } .join "\n"
|
9
44
|
end
|
10
45
|
|
11
|
-
#
|
12
|
-
def
|
13
|
-
|
14
|
-
super(key) || super(key.to_s) || @request.cookies[key] || @request.cookies[key.to_s]
|
15
|
-
elsif key.is_a? String
|
16
|
-
super(key) || super(key.to_sym) || @request.cookies[key] || @request.cookies[key.to_sym]
|
17
|
-
else
|
18
|
-
super(key) || @request.cookies[key]
|
19
|
-
end
|
46
|
+
# Returns an array with all the keys of any available cookies (both existing and new cookies).
|
47
|
+
def keys
|
48
|
+
(@request ? (super + request.cookies.keys) : super)
|
20
49
|
end
|
21
50
|
|
22
|
-
#
|
23
|
-
def
|
24
|
-
|
25
|
-
@response.delete_cookie key
|
26
|
-
delete key
|
27
|
-
if key.is_a? Symbol
|
28
|
-
delete key.to_s
|
29
|
-
elsif key.is_a? String
|
30
|
-
delete key.to_sym
|
31
|
-
end
|
32
|
-
return nil
|
33
|
-
end
|
34
|
-
@response.set_cookie key, value
|
35
|
-
value = value[:value] if value.is_a? Hash
|
36
|
-
super
|
51
|
+
# Returns an array with all the values of any available cookies (both existing and new cookies).
|
52
|
+
def values
|
53
|
+
(@request ? (super + request.cookies.values) : super)
|
37
54
|
end
|
38
|
-
|
39
|
-
# Writes a line dlimited string of all the existing and the new cookies. i.e.:
|
40
|
-
# name1=value1
|
41
|
-
# name2=value2
|
42
|
-
def to_s
|
43
|
-
(@request ? (self.to_a + request.cookies.to_a) : self.to_a).map! {|pair| pair.join('=') } .join ("\n")
|
44
|
-
end
|
45
|
-
# Returns an array with all the keys of any available cookies (both existing and new cookies).
|
46
|
-
def keys
|
47
|
-
(@request ? (super + request.cookies.keys) : super)
|
48
|
-
end
|
49
|
-
# Returns an array with all the values of any available cookies (both existing and new cookies).
|
50
|
-
def values
|
51
|
-
(@request ? (super + request.cookies.values) : super)
|
52
|
-
end
|
53
|
-
end
|
55
|
+
end
|
54
56
|
end
|