plezi 0.10.12 → 0.10.13

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.
@@ -29,6 +29,8 @@ module Plezi
29
29
  def self.included base
30
30
  base.send :include, InstanceMethods
31
31
  base.extend ClassMethods
32
+ base.superclass.instance_eval {extend SuperClassMethods}
33
+ base.send :include, Plezi::Base::WSObject
32
34
  end
33
35
 
34
36
  #the methods here will be injected to the Placebo controller as Instance methods.
@@ -45,55 +47,16 @@ module Plezi
45
47
  return super() if defined? super
46
48
  GR.warn "Placebo #{self.class.superclass.name} disconnected. Ignore if this message appears during shutdown."
47
49
  end
48
-
49
- # handles broadcasts / unicasts
50
- def on_broadcast ws
51
- data = ws.data
52
- unless (data[:type] || data[:target]) && data[:method] && data[:data]
53
- GReactor.warn "Broadcast message unknown... falling back on base broadcasting"
54
- return super(data) if defined? super
55
- return false
56
- end
57
- # return false if data[:type] && !self.is_a?(data[:type])
58
- return false if data[:target] && data[:target] != ws.uuid
59
- return false unless self.class.has_super_method?(data[:method])
60
- self.method(data[:method]).call *data[:data]
61
- end
62
- # Returns the websocket connection's UUID, used for unicasting.
63
- def uuid
64
- io[:uuid] ||= SecureRandom.uuid
65
- end
66
-
67
- # Performs a websocket unicast to the specified target.
68
- def unicast target_uuid, method_name, *args
69
- GRHttp::Base::WSHandler.unicast target_uuid, data: args, target: target_uuid, method: method_name
70
- __send_to_redis data: args, target: target_uuid, method: method_name
71
- end
72
- # broadcast to a specific controller
73
- def broadcast controller_class, method_name, *args
74
- GRHttp::Base::WSHandler.broadcast({data: args, type: controller_class, method: method_name}, self)
75
- __send_to_redis data: args, type: controller_class, method: method_name
76
- end
77
- # multicast to all handlers.
78
- def multicast method_name, *args
79
- GRHttp::Base::WSHandler.broadcast({method: method_name, data: args, type: :all}, self)
80
- __send_to_redis method: method_name, data: args, type: :all
81
- end
82
- protected
83
- def __send_to_redis data
84
- raise "Wrong method name for websocket broadcasting - expecting type Symbol" unless data[:method].is_a?(Symbol) || data[:method].is_a?(Symbol)
85
- conn = Plezi.redis_connection
86
- data[:server] = Plezi::Settings.uuid
87
- return conn.publish( Plezi::Settings.redis_channel_name, data.to_yaml ) if conn
88
- false
50
+ def placebo?
51
+ true
89
52
  end
90
53
  end
91
54
  #the methods here will be injected to the Placebo controller as class methods.
92
55
  module ClassMethods
93
- public
94
- def has_super_method? method_name
95
- @super_methods_list ||= self.superclass.instance_methods.to_set
96
- @super_methods_list.include? method_name
56
+ end
57
+ module SuperClassMethods
58
+ def placebo?
59
+ true
97
60
  end
98
61
  end
99
62
  end
@@ -0,0 +1,204 @@
1
+ module Plezi
2
+
3
+ # the methods defined in this module will be injected into the Controller class passed to
4
+ # Plezi (using the `route` or `shared_route` commands), and will be available
5
+ # for the controller to use within it's methods.
6
+ #
7
+ # for some reason, the documentation ignores the following additional attributes, which are listed here:
8
+ #
9
+ # request:: the HTTPRequest object containing all the data from the HTTP request. If a WebSocket connection was established, the `request` object will continue to contain the HTTP request establishing the connection (cookies, parameters sent and other information).
10
+ # params:: any parameters sent with the request (short-cut for `request.params`), will contain any GET or POST form data sent (including file upload and JSON format support).
11
+ # cookies:: a cookie-jar to get and set cookies (set: `cookie\[:name] = data` or get: `cookie\[:name]`). Cookies and some other data must be set BEFORE the response's headers are sent.
12
+ # flash:: a temporary cookie-jar, good for one request. this is a short-cut for the `response.flash` which handles this magical cookie style.
13
+ # response:: the HTTPResponse **OR** the WSResponse object that formats the response and sends it. use `response << data`. This object can be used to send partial data (such as headers, or partial html content) in blocking mode as well as sending data in the default non-blocking mode.
14
+ # host_params:: a copy of the parameters used to create the host and service which accepted the request and created this instance of the controller class.
15
+ #
16
+ module Base
17
+
18
+ # This module includes all the methods that will be injected into Websocket objects,
19
+ # specifically into Plezi Controllers and Placebo objects.
20
+ module WSObject
21
+ def self.included base
22
+ base.send :include, InstanceMethods
23
+ base.extend ClassMethods
24
+ base.superclass.instance_eval {extend SuperClassMethods}
25
+ end
26
+
27
+ module InstanceMethods
28
+ public
29
+ # handles broadcasts / unicasts
30
+ def on_broadcast ws
31
+ data = ws.data
32
+ unless (data[:type] || data[:target]) && data[:method] && data[:data]
33
+ GReactor.warn "Broadcast message unknown... falling back on base broadcasting"
34
+ return super(data) if defined? super
35
+ return false
36
+ end
37
+ return false if data[:type] && data[:type] != :all && !self.is_a?(data[:type])
38
+ return ((data[:type] == :all) ? false : (raise "Broadcasting recieved but no method can handle it - dump:\r\n #{data.to_s}") ) unless self.class.has_super_method?(data[:method])
39
+ self.method(data[:method]).call *data[:data]
40
+ end
41
+
42
+ # Performs a websocket unicast to the specified target.
43
+ def unicast target_uuid, method_name, *args
44
+ self.class.unicast target_uuid, method_name, *args
45
+ end
46
+
47
+ # Use this to brodcast an event to all 'sibling' objects (websockets that have been created using the same Controller class).
48
+ #
49
+ # Accepts:
50
+ # method_name:: a Symbol with the method's name that should respond to the broadcast.
51
+ # args*:: The method's argumenst - It MUST be possible to stringify the arguments into a YAML string, or broadcasting and unicasting will fail when scaling beyond one process / one machine.
52
+ #
53
+ # The method will be called asynchrnously for each sibling instance of this Controller class.
54
+ #
55
+ def broadcast method_name, *args
56
+ return false unless self.class.has_method? method_name
57
+ self.class._inner_broadcast({ method: method_name, data: args, type: self.class}, __get_io )
58
+ end
59
+ # Use this to multicast an event to ALL websocket connections on EVERY controller, including Placebo controllers.
60
+ #
61
+ # Accepts:
62
+ # method_name:: a Symbol with the method's name that should respond to the broadcast.
63
+ # args*:: The method's argumenst - It MUST be possible to stringify the arguments into a YAML string, or broadcasting and unicasting will fail when scaling beyond one process / one machine.
64
+ #
65
+ # The method will be called asynchrnously for ALL websocket connections.
66
+ #
67
+ def multicast method_name, *args
68
+ self.class._inner_broadcast({ method: method_name, data: args, type: :all}, __get_io )
69
+ end
70
+
71
+ # Get's the websocket's unique identifier for unicast transmissions.
72
+ #
73
+ # This UUID is also used to make sure Radis broadcasts don't triger the
74
+ # boadcasting object's event.
75
+ def uuid
76
+ return @uuid if @uuid
77
+ if @response && @response.is_a?(GRHttp::WSEvent)
78
+ return (@uuid ||= @response.uuid + Plezi::Settings.uuid)
79
+ elsif @io
80
+ return (@uuid ||= (@io[:uuid] ||= SecureRandom.uuid) + Plezi::Settings.uuid)
81
+ end
82
+ nil
83
+ end
84
+ alias :unicast_id :uuid
85
+
86
+ protected
87
+ def __get_io
88
+ @io ||= (@request ? @request.io : nil)
89
+ end
90
+ end
91
+ module ClassMethods
92
+
93
+ def reset_routing_cache
94
+ @methods_list = nil
95
+ @exposed_methods_list = nil
96
+ @super_methods_list = nil
97
+ has_method? nil
98
+ has_exposed_method? nil
99
+ has_super_method? nil
100
+ end
101
+ def has_method? method_name
102
+ @methods_list ||= self.instance_methods.to_set
103
+ @methods_list.include? method_name
104
+ end
105
+ def has_super_method? method_name
106
+ @super_methods_list ||= self.superclass.instance_methods.to_set
107
+ @super_methods_list.include? method_name
108
+ end
109
+ def has_exposed_method? method_name
110
+ @exposed_methods_list ||= ( (self.public_instance_methods - Class.new.instance_methods - Plezi::ControllerMagic::InstanceMethods.instance_methods - [:before, :after, :save, :show, :update, :delete, :initialize, :on_message, :on_broadcast, :pre_connect, :on_open, :on_close]).delete_if {|m| m.to_s[0] == '_'} ).to_set
111
+ @exposed_methods_list.include? method_name
112
+ end
113
+ protected
114
+
115
+ # a callback that resets the class router whenever a method (a potential route) is added
116
+ def method_added(id)
117
+ reset_routing_cache
118
+ end
119
+ # a callback that resets the class router whenever a method (a potential route) is removed
120
+ def method_removed(id)
121
+ reset_routing_cache
122
+ end
123
+ # a callback that resets the class router whenever a method (a potential route) is undefined (using #undef_method).
124
+ def method_undefined(id)
125
+ reset_routing_cache
126
+ end
127
+
128
+ end
129
+
130
+ module SuperClassMethods
131
+ public
132
+
133
+ # WebSockets: fires an event on all of this controller's active websocket connections.
134
+ #
135
+ # Class method.
136
+ #
137
+ # Use this to brodcast an event to all connections.
138
+ #
139
+ # accepts:
140
+ # method_name:: a Symbol with the method's name that should respond to the broadcast.
141
+ # *args:: any arguments that should be passed to the method (IF REDIS IS USED, LIMITATIONS APPLY).
142
+ #
143
+ # this method accepts and optional block (NON-REDIS ONLY) to be used as a callback for each sibling's event.
144
+ #
145
+ # the method will be called asynchrnously for each sibling instance of this Controller class.
146
+ def broadcast method_name, *args
147
+ return false unless has_method? method_name
148
+ _inner_broadcast method: method_name, data: args, type: self
149
+ end
150
+
151
+ # WebSockets: fires an event on a specific websocket connection using it's UUID.
152
+ #
153
+ # Use this to unidcast an event to specific websocket connection using it's UUID.
154
+ #
155
+ # accepts:
156
+ # target_uuid:: the target's unique UUID.
157
+ # method_name:: a Symbol with the method's name that should respond to the broadcast.
158
+ # *args:: any arguments that should be passed to the method (IF REDIS IS USED, LIMITATIONS APPLY).
159
+ def unicast target_uuid, method_name, *args
160
+ raise 'No target specified for unicasting!' unless target_uuid
161
+ @@uuid_cutoff ||= Plezi::Settings.uuid.length
162
+ _inner_broadcast method: method_name, data: args, target: target_uuid[0...@@uuid_cutoff], to_server: target_uuid[@@uuid_cutoff..-1]
163
+ end
164
+
165
+ # Use this to multicast an event to ALL websocket connections on EVERY controller, including Placebo controllers.
166
+ #
167
+ # Accepts:
168
+ # method_name:: a Symbol with the method's name that should respond to the broadcast.
169
+ # args*:: The method's argumenst - It MUST be possible to stringify the arguments into a YAML string, or broadcasting and unicasting will fail when scaling beyond one process / one machine.
170
+ #
171
+ # The method will be called asynchrnously for ALL websocket connections.
172
+ #
173
+ def multicast method_name, *args
174
+ _inner_broadcast method: method_name, data: args, type: :all
175
+ end
176
+
177
+ # WebSockets
178
+
179
+ # sends the broadcast
180
+ def _inner_broadcast data, ignore_io = nil
181
+ if data[:target]
182
+ return ( (data[:to_server].nil? || data[:to_server] == Plezi::Settings.uuid) ? GRHttp::Base::WSHandler.unicast(data[:target], data) : false ) || __inner_redis_broadcast(data)
183
+ else
184
+ GRHttp::Base::WSHandler.broadcast data, ignore_io
185
+ __inner_redis_broadcast data
186
+ end
187
+ true
188
+ end
189
+
190
+ def __inner_redis_broadcast data
191
+ return unless conn = Plezi.redis_connection
192
+ data[:server] = Plezi::Settings.uuid
193
+ return conn.publish( ( data[:to_server] ? data[:to_server] : Plezi::Settings.redis_channel_name ), data.to_yaml ) if conn
194
+ false
195
+ end
196
+
197
+ def has_method? method_name
198
+ @methods_list ||= self.instance_methods.to_set
199
+ @methods_list.include? method_name
200
+ end
201
+ end
202
+ end
203
+ end
204
+ end
@@ -35,7 +35,7 @@ module Plezi
35
35
  #
36
36
  # returns true if data was sent.
37
37
  def send_static_file request, response
38
- root = request.io[:params][:root]
38
+ root = request.io[:params][:public]
39
39
  return false unless root
40
40
  file_requested = request[:path].to_s.split('/')
41
41
  unless file_requested.include? '..'
@@ -1,3 +1,3 @@
1
1
  module Plezi
2
- VERSION = "0.10.12"
2
+ VERSION = "0.10.13"
3
3
  end
@@ -8,8 +8,8 @@ Gem::Specification.new do |spec|
8
8
  spec.version = Plezi::VERSION
9
9
  spec.authors = ["Boaz Segev"]
10
10
  spec.email = ['boaz@2be.co.il']
11
- spec.summary = %q{Plezi is the native Ruby Framework for real time web-apps. An easy way to write Websockets, RESTful routing and HTTP streaming apps.}
12
- spec.description = %q{Plezi is the native Ruby Framework for real time web-apps. An easy way to write Websockets, RESTful routing and HTTP streaming apps.}
11
+ spec.summary = %q{Plezi - the easy way to add Websockets, RESTful routing and HTTP streaming serviced to Ruby Web-Apps.}
12
+ spec.description = %q{Plezi - the easy way to add Websockets, RESTful routing and HTTP streaming serviced to Ruby Web-Apps.}
13
13
  spec.homepage = "http://boazsegev.github.io/plezi/"
14
14
  spec.license = "MIT"
15
15
 
@@ -18,7 +18,7 @@ Gem::Specification.new do |spec|
18
18
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
19
  spec.require_paths = ["lib"]
20
20
 
21
- spec.add_dependency "grhttp", "~> 0.0.15"
21
+ spec.add_dependency "grhttp", "~> 0.0.16"
22
22
  spec.add_development_dependency "bundler", "~> 1.7"
23
23
  spec.add_development_dependency "rake", "~> 10.0"
24
24
 
@@ -1,57 +1,114 @@
1
1
  <!DOCTYPE html>
2
2
  <head>
3
3
  <style>
4
+ /*
5
+ med-blue: #44518E
6
+ dark-gray: #424A70
7
+ blue: #1B2864
8
+ light-blue: #818ECE
9
+ light-gray: #99A3CE
10
+ */
4
11
  body, html
5
12
  {
6
- background-color: #eee;
7
- padding: 0; margin: 0;
8
- width: 100%;
13
+ background-color: #99A3CE;
14
+ padding: 0; margin: 0;
15
+ width: 100%;
16
+ font-size: 1em;
9
17
  }
18
+
10
19
  h1
11
20
  {
12
- background-color: #ddd;
13
- color: #008;
14
- text-align: center;
15
- border-bottom: 1px solid #000;
16
- margin: 0;
17
- padding: 0.5em;
18
- width: auto;
21
+ font-family: 'Shadows Into Light', cursive;
22
+ background-color: #1B2864;
23
+ color: #99A3CE;
24
+ text-align: center;
25
+ border-bottom: 4px solid #424A70;
26
+ margin: 0 0 1em 0;
27
+ padding: 0.4em 0;
28
+ width: 100%;
29
+ }
30
+ h2
31
+ {
32
+ background-color: #44518E;
33
+ color: #C6CCE7;
34
+ text-align: left;
35
+ margin: 0 0 1em 0;
36
+ padding: 0.5em 5%;
37
+ border-radius: 20px 20px 0 0;
38
+ font-family: 'Architects Daughter', cursive;
39
+ border-bottom: 3px solid #424A70;
40
+ font-size: 1.2em;
41
+ }
42
+ h3
43
+ {
44
+ font-family: 'Architects Daughter', cursive;
45
+ background-color: #44518E;
46
+ color: #C6CCE7;
47
+ text-align: left;
48
+ margin: 0 0 1em 0;
49
+ padding: 0.5em 5%;
50
+ border-radius: 1em 1em 0 0;
51
+ border-bottom: 2px solid #424A70;
52
+ font-size: 1.1em;
53
+ }
54
+ h2:before {
55
+ content: "|||";
56
+ color: #99A3CE;
57
+ padding-right: 0.3em;
58
+ margin-left: -0.5em;
59
+ }
60
+ h3:before {
61
+ content: "|||||";
62
+ color: #99A3CE;
63
+ padding-right: 0.3em;
64
+ margin-left: -1em;
65
+ }
66
+ h1 a, h2 a, h3 a
67
+ {
68
+ color: #EBCD86;
69
+ }
70
+ h1 a:hover, h2 a:hover, h3 a:hover
71
+ {
72
+ color: #EBD7A6;
19
73
  }
20
74
  p
21
75
  {
22
- color:#004;
23
- font-size: 1.2em;
24
- padding: 0 1em;
76
+ font-size: 1em;
77
+ padding: 0 1em;
78
+ margin: 0.5em 0;
25
79
  }
26
- #wrapper
80
+ a
81
+ {
82
+ color: #D0AC54;
83
+ text-decoration: none;
84
+ }
85
+ a:hover
27
86
  {
28
- background-color: #fff;
29
- margin: 1em 5%;
30
- padding: 0 0 2% 0;
31
- border-radius: 20px;
32
- min-height: 50%;
33
- color: #007;
87
+ color: #927121;
88
+ text-decoration: underline;
34
89
  }
35
- #wrapper h2
90
+ #wrapper
36
91
  {
37
- background-color: #ddd;
38
- color: #008;
39
- text-align: center;
40
- margin: 0 0 1em 0;
41
- padding: 0.5em 0;
42
- width: 100%;
43
- border-radius: 20px;
92
+ background-color: #fff;
93
+ margin: 1em 5%;
94
+ padding: 0 0 2%;
95
+ border-radius: 20px;
96
+ min-height: 50%;
97
+ color: #007;
44
98
  }
99
+
45
100
  #wrapper p{ padding: 0 2%;}
46
- #wrapper #missing
101
+ .bold { font-weight: bold; }
102
+ #wrapper ol li{ padding: 0 2%;}
103
+ pre
104
+ {
105
+ border-radius: 20px;
106
+ padding: 0.5em 0;
107
+ background-color: #444;
108
+ color: #ddd;
109
+ }
110
+ @media screen and (max-width: 680px)
47
111
  {
48
- color:#904;
49
- font-size: 1.4em;
50
- padding: 0.5em 0;
51
- text-align: center;
52
- background-color: #fee;
53
- border-bottom: 1px solid #800;
54
- margin: 0;
55
112
  }
56
113
  </style>
57
114
  </head>