plezi 0.11.0 → 0.11.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.
@@ -1,3 +1,3 @@
1
1
  module Plezi
2
- VERSION = "0.11.0"
2
+ VERSION = "0.11.1"
3
3
  end
@@ -119,7 +119,7 @@ class SampleController
119
119
  # the demo content simply broadcasts the message.
120
120
  def on_message data
121
121
  # broadcast sends an asynchronous message to all sibling instances, but not to self.
122
- data = Plezi::HTTP.escape data
122
+ data = ERB::Util.html_escape data
123
123
  broadcast :_print_out, data
124
124
  response << "You said: #{data}"
125
125
  response << (request.ssl? ? "FYI: Yes, This is an SSL connection..." : "FYI: Nope, this isn't an SSL connection (clear text).") if data.match /ssl\?/i
@@ -40,18 +40,21 @@ class MyController
40
40
  end
41
41
  # Websockets
42
42
  def on_message data
43
- data = "#{params[:id]}: #{data}" if params[:id]
44
- _print data
45
- broadcast :_print, data
43
+ data = ERB::Util.html_escape data
44
+ print data
45
+ broadcast :print, data
46
46
  end
47
47
  def on_open
48
- _print 'Welcome!'
49
- broadcast :_print, "Somebody joind us :-)"
48
+ print 'Welcome!'
49
+ broadcast :print, "Somebody joind us :-)"
50
50
  end
51
51
  def on_close
52
- broadcast :_print, "Somebody left us :-("
52
+ broadcast :print, "Somebody left us :-("
53
53
  end
54
- def _print data
54
+
55
+ protected
56
+
57
+ def print data
55
58
  response << data
56
59
  end
57
60
  end
@@ -32,7 +32,7 @@ That's it.
32
32
 
33
33
  ##The Ruby Code (chatroom server)
34
34
 
35
- We can create an Plezi application using the `$ plezi new myapp` command, but that's too easy - we want it hardcore.
35
+ We can create an Plezi application using the `$ plezi new myapp` or `plezi mini myapp` commands, but that's too easy - we want it hardcore.
36
36
 
37
37
  Let's create an application folder called `mychat` and save our code in a file called `mychat.rb` in our application folder.
38
38
 
@@ -73,7 +73,7 @@ A service, in this case, is realy just a nice word for the Plezi server (which m
73
73
 
74
74
  As you can see, some options are there for later, but are disabled for now.
75
75
 
76
- - **root**: this option defines the folder from which Plezi should serve static files (html files, images etc'). We will not be serving any static files at the moment, so this option is disabled.
76
+ - **public**: this option defines the folder from which Plezi should serve public static files (html files, images etc'). We will not be serving any static files at the moment, so this option is disabled.
77
77
 
78
78
  - **assets**: this option tells plezi where to look for asset files that might need rendering - such as Sass and Coffee-Script files... We will not be using these features either, so that's out as well.
79
79
 
@@ -83,11 +83,13 @@ As you can see, some options are there for later, but are disabled for now.
83
83
 
84
84
  - **ssl**: this option, if set to true, will make our service into an SSL/TSL encrypted service (as well as our websocket service)... we can leave this off for now - it's actually hardly ever used since it's usually better to leave that to our production server.
85
85
 
86
+ - **port**: Hardcoding a port would override the default port (which is either 3000 or the default port specified using the `-p <port>`). For this demo, as in most cases, it's best to a avoid setting up a port and preffer the default preferance.
87
+
86
88
  ```ruby
87
89
  service_options = {
88
- # root: Root.join('public').to_s,
90
+ # public: Root.join('public').to_s,
89
91
  # assets: Root.join('assets').to_s,
90
- # assets_public: '/',
92
+ # assets_public: '/assets',
91
93
  templates: Root.join('views').to_s,
92
94
  ssl: false
93
95
  }
@@ -95,13 +97,13 @@ service_options = {
95
97
 
96
98
  Next we call the `listen` command - this command actually creates the service.
97
99
 
98
- The port plezi uses by default is 3000 [http://localhost:3000/](http://localhost:3000/). By not defining a port, we allowed ourselves to either use the default port (3000) or decide the port when we run our application (i.e. `./mychat.rb -p 8080`).
100
+ The port plezi uses by default is either 3000 [http://localhost:3000/](http://localhost:3000/) or the port defined when calling the script (i.e. `./mychat.rb -p 8080`).
99
101
 
100
102
  ```ruby
101
103
  listen service_options
102
104
  ```
103
105
 
104
- (if you want to force a specific port, i.e. 80, write `listen 80, service_options` - but make sure you are allowed to use this port)
106
+ (if you want to force a specific port, i.e. 80, write `listen service_options.merge(port: 80)` - but make sure you are allowed to use this port)
105
107
 
106
108
  Last, but not least, we tell Plezi to connect the root of our web application to our ChatController - in other words, make sure the root _path_ ('/') is connected to the ChatController class.
107
109
 
@@ -109,9 +111,9 @@ Last, but not least, we tell Plezi to connect the root of our web application to
109
111
  route '/', ChatController
110
112
  ```
111
113
 
112
- Plezi controller classes are like virtual folders with special support for RESTful methods (`index`, `new`, `save`, `update`, `delete`), HTTP filters and helpers (`before`, `after`, `redirect_to`, `send_data`), WebSockets methods (`on_open`, `on_message(data)`, `on_close`), and WebSockets filters and helpers (`pre-connect`, `broadcast`, `unicast` etc').
114
+ Plezi controller classes are like virtual folders with special support for RESTful methods (`index`, `new`, `save`, `update`, `delete`), HTTP filters and helpers (`before`, `after`, `redirect_to`, `send_data`), WebSockets methods (`on_open`, `on_message(data)`, `on_close`), and WebSockets filters and helpers (`pre_connect`, `broadcast`, `unicast` etc').
113
115
 
114
- Plezi uses a common special parameter called 'id' to help with all this magic... if we don't define this parameter ourselves, Plezi will try to append this parameter to the end our route's path. So, actually, our route looks like this:
116
+ Plezi uses a common special parameter called 'id' to help with all this magic... if we don't define this parameter ourselves, Plezi will try to append this parameter as an optional parameter to the end our route's path. So, actually, our route looks like this:
115
117
 
116
118
  ```ruby
117
119
  route '/(:id)', ChatController
@@ -133,7 +135,7 @@ def index
133
135
  end
134
136
  ```
135
137
 
136
- Plezi has a really easy method called `render` that creates (and caches) a rendering object with our template file's content and returns a String of our rendered template.
138
+ Plezi has a really easy method called `render` that creates (and caches) a rendering object with our template file's content and returns a String with our rendered template.
137
139
 
138
140
  Lets fill in our `index` method:
139
141
 
@@ -155,36 +157,38 @@ Let's rewrite our `index` method to make it cleaner:
155
157
  class ChatController
156
158
  def index
157
159
  response['content-type'] = 'text/html'
158
- render(:chat)
160
+ render(:chat) # since this String is the returned value, it works.
159
161
  end
160
162
  end
161
163
  ```
162
164
 
163
165
  When someone will visit the root of our application (which is also the '_root_' of our controller), they will get the our ChatController#index method.
164
166
 
165
- We just need to remember to create a 'chat' template file (`chat.html.erb` or `chat.html.haml`)... but that's for later.
167
+ We just need to remember to create a 'chat' template file (`chat.html.erb`, `chat.html.slim` or `chat.html.haml`)... but that's for later.
166
168
 
167
169
  ####Telling people that we made this cool app!
168
170
 
169
- there is a secret web convention that allows developers to _sign_ their work by answering the `/people` path with plain text and the names of the people who built the site...
171
+ there is a secret web convention that allows developers to _sign_ their work by answering the `/people.txt` path with plain text and the names of the people who built the site...
170
172
 
171
173
  With Plezi, that's super easy.
172
174
 
173
- Since out ChatController is at the root of ou application, let's add a `people` method to our ChatController:
175
+ Since out ChatController is at the root of our application, let's add a `people.txt` method to our ChatController:
176
+
177
+ method names cant normally have the dot in their name, do we will use a helper method for this special name.
174
178
 
175
179
  ```ruby
176
- def people
180
+ def_special_method "people.txt" do
177
181
  "I wrote this app :)"
178
182
  end
179
183
  ```
180
184
 
181
- Plezi uses the 'id' parameter to recognize special paths as well as for it's RESTful support. Now, anyone visiting '/people' will reach our ChatController#people method.
185
+ Plezi uses the 'id' parameter to recognize special paths as well as for it's RESTful support. Now, anyone visiting '/people.txt' will reach our ChatController#people method.
182
186
 
183
- Just like we already discovered, returning a String object (the last line of the `people` method is a String) automatically appends this string to our HTTP response - cool :)
187
+ Just like we already discovered, returning a String object (the last line of the `people.txt` method is a String) automatically appends this string to our HTTP response - cool :)
184
188
 
185
189
  ###The Controller - live input and pushing data (WebSockets)
186
190
 
187
- We are building an advanced application here - this is _not_ another 'hello world' - lets start exploring the advanced stuff.
191
+ We are building a somewhat advanced application here - this is _not_ another 'hello world' - lets start exploring the advanced stuff.
188
192
 
189
193
  ####Supporting WebSockets
190
194
 
@@ -212,14 +216,14 @@ end
212
216
 
213
217
  To design a chatroom we will need a few things:
214
218
 
215
- 1. We will need to force people identify themselves by choosing nicknames - to do this we will define the `on_connect` method to refuse any connections that don't have a nickname.
219
+ 1. We will need to force people identify themselves by choosing nicknames - to do this we will define the `on_open` method to refuse any connections that don't have a nickname.
216
220
  2. We will want to make sure these nicknames are unique and don't give a wrong sense of authority (nicknames such as 'admin' should be forbidden) - for now, we will simply refuse the 'wrong' type of nicknames and leave uniqieness for another time.
217
221
  3. We will want to push messages we recieve to all the other chatroom members - to do this we will use the `broadcast` method in our `on_message(data)` method.
218
- 4. We will also want to tell people when someone left the chatroom - to do this we can define an `on_disconnect` method and use the `broadcast` method in there.
222
+ 4. We will also want to tell people when someone left the chatroom - to do this we can define an `on_close` method and use the `broadcast` method in there.
219
223
 
220
224
  We can use the :id parameter to set the nickname.
221
225
 
222
- the :id is an automatic parameter that Plezi appended to our path like already explained and it's perfect for our simple needs.
226
+ the :id is an automatic parameter that Plezi appended to our path like already explained and it's perfect for our current needs.
223
227
 
224
228
  We could probably rewrite our route to something like this: `route '/(:id)/(:nickname)', ChatController` (or move the `/people` path out of the controller and use `'/(:nickname)'`)... but why work hard when we don't need to?
225
229
 
@@ -239,7 +243,7 @@ def on_message data
239
243
  return false
240
244
  end
241
245
  message = {}
242
- message[:message] = GRHttp.HTTP.escape data['message'] # we should sanitize the data
246
+ message[:message] = data['message'] # should consider sanitizing this
243
247
  message[:event] = :chat
244
248
  message[:from] = params[:id]
245
249
  message[:at] = Time.now
@@ -247,7 +251,7 @@ def on_message data
247
251
  end
248
252
  ```
249
253
 
250
- let's write it a bit shorter... if our code has nothing important to say, it might as well be quick about it.
254
+ let's write it a bit shorter... if our code has nothing important to say, it might as well be quick about it and avoid unnecessary intermediate object assignments.
251
255
 
252
256
  ```ruby
253
257
  def on_message data
@@ -258,7 +262,7 @@ def on_message data
258
262
  response.close
259
263
  return false
260
264
  end
261
- broadcast :_send_message, {event: :chat, from: params[:id], message: GRHttp.HTTP.escape(data['message']), at: Time.now}.to_json
265
+ broadcast :_send_message, {event: :chat, from: params[:id], message: ERB::Util.html_escape(data['message']), at: Time.now}.to_json
262
266
  end
263
267
  ```
264
268
 
@@ -298,7 +302,7 @@ Another feature we want to put in, is letting people know when someone enters or
298
302
  Using the `broadcast` method with the special `on_disconnect` websocket method, makes telling people we left an easy task...
299
303
 
300
304
  ```ruby
301
- def on_disconnect
305
+ def on_close
302
306
  message = {event: :chat, from: '', at: Time.now}
303
307
  message[:message] = "#{params[:id]} left the chatroom."
304
308
  broadcast :_send_message, message.to_json if params[:id]
@@ -310,14 +314,14 @@ We will only tell people that we left the chatroom if our login was successful -
310
314
  Let's make it a bit shorter?
311
315
 
312
316
  ```ruby
313
- def on_disconnect
317
+ def on_close
314
318
  broadcast :_send_message, {event: :chat, from: '', at: Time.now, message: "#{params[:id]} left the chatroom."}.to_json if params[:id]
315
319
  end
316
320
  ```
317
321
 
318
322
  ####The login process and telling people we're here
319
323
 
320
- If we ever write a real chatroom, our login process will look somewhat different - but the following process is good enough for now and it has a lot to teach us...
324
+ If we ever write a real chatroom, our login process will look somewhat different, probably using the `pre_connect` callback (which is safer) - but the following process is good enough for now and it has a lot to teach us...
321
325
 
322
326
  First, we will ensure the new connection has a nickname (the connection was made to '/nickname' rather then the root of our application '/'):
323
327
 
@@ -342,15 +346,16 @@ def pre_connect
342
346
  end
343
347
  ```
344
348
 
345
- Since Websocket connections start as an HTTP GET request, the pre-connect is called while still in 'HTTP mode', allowing us to use HTTP logic and refuse connections even before any websocket data can be sent by the 'client'. This is definitly the safer approach.
349
+ Since Websocket connections start as an HTTP GET request, the pre-connect is called while still in 'HTTP mode', allowing us to use HTTP logic and refuse connections even before any websocket data can be sent by the 'client'. This is definitly the safer approach... but it doesn't allow us to send websocket data (such as our pre-close message).
346
350
 
347
351
  Next, we will check if the nickname is on the reserved names list, to make sure nobody impersonates a system administrator... let's add this code to our `on_open` method:
348
352
 
349
353
  ```ruby
350
354
  message = {from: '', at: Time.now}
351
- if params[:id].match /admin|admn|system/i
355
+ name = params[:id]
356
+ if (name.match(/admin|admn|system|sys|administrator/i))
352
357
  message[:event] = :error
353
- message[:message] = "The nickname '#{params[:id]}' is refused."
358
+ message[:message] = "The nickname '#{name}' is refused."
354
359
  response << message.to_json
355
360
  params[:id] = false
356
361
  response.close
@@ -358,63 +363,50 @@ Next, we will check if the nickname is on the reserved names list, to make sure
358
363
  end
359
364
  ```
360
365
 
361
- Then, if all is good, we will welcome the new connection to our chatroom. We will also tell the new guest who is already connected and broadcast their arrivale to everybody else...:
366
+ Then, if all is good, we will welcome the new connection to our chatroom. We will also broadcast the new guest's arrivale to everybody else...:
362
367
 
363
368
  ```ruby
364
369
  message = {from: '', at: Time.now}
365
370
  message[:event] = :chat
371
+ message[:message] = "Welcome #{params[:id]}."
366
372
  response << message.to_json
367
373
  message[:message] = "#{params[:id]} joined the chatroom."
368
374
  broadcast :_send_message, message.to_json
369
375
  ```
370
376
 
371
- Let's make it just a bit shorter, most of the code ins't important enough to worry about readability... we can compact our `if` statement to an inline statement like this:
372
377
 
373
- ```ruby
374
- message[:message] = list.empty? ? "You're the first one here." : "#{list[0..-2].join(', ')} #{list[1] ? 'and' : ''} #{list.last} #{list[1] ? 'are' : 'is'} already in the chatroom"
375
- ```
376
-
377
- We will also want to tweek the code a bit, so the nicknames are case insensative...
378
-
379
- This will be our final `on_connect` method:
378
+ This will be our final `on_open` method:
380
379
 
381
380
  ```ruby
382
- def on_connect
381
+ def on_open
383
382
  if params[:id].nil?
384
383
  response << {event: :error, from: :system, at: Time.now, message: "Error: cannot connect without a nickname!"}.to_json
385
384
  response.close
386
385
  return false
387
386
  end
388
387
  message = {from: '', at: Time.now}
389
- list = collect(:_ask_nickname)
390
- if ((list.map {|n| n.downcase}) + ['admin', 'system', 'sys', 'administrator']).include? params[:id].downcase
388
+ name = params[:id]
389
+ if (name.match(/admin|admn|system|sys|administrator/i))
391
390
  message[:event] = :error
392
- message[:message] = "The nickname '#{params[:id]}' is already taken."
391
+ message[:message] = "The nickname '#{name}' is already taken."
393
392
  response << message.to_json
394
393
  params[:id] = false
395
394
  response.close
396
395
  return
397
396
  end
398
397
  message[:event] = :chat
399
- message[:message] = list.empty? ? "You're the first one here." : "#{list[0..-2].join(', ')} #{list[1] ? 'and' : ''} #{list.last} #{list[1] ? 'are' : 'is'} already in the chatroom"
398
+ message[:message] = "Welcome #{params[:id]}."
399
+ # Should you end up storing your connected user names inside a manged list
400
+ # in redis or a database and then read that into a variable called 'list'
401
+ # here is some code you can use to write a message to the user based on the
402
+ # people currently in that list.
403
+ # message[:message] = list.empty? ? "You're the first one here." : "#{list[0..-2].join(', ')} #{list[1] ? 'and' : ''} #{list.last} #{list[1] ? 'are' : 'is'} already in the chatroom"
400
404
  response << message.to_json
401
- message[:message] = "#{params[:id]} joined the chatroom."
405
+ message[:message] = "#{name} joined the chatroom."
402
406
  broadcast :_send_message, message.to_json
403
407
  end
404
408
  ```
405
409
 
406
- ####The \_ask_nickname method
407
-
408
- Just like the `_send_message` method, this method's name starts with an underscore to make sure it is ignored by the Plezi router.
409
-
410
- Since this message is used by the `collect` method to collect information (which will block our code), it's very important that this method will be short and fast - it might run hundreds of times (or more), depending how many people are connected to our chatroom...
411
-
412
- ```ruby
413
- def _ask_nickname
414
- return params[:id]
415
- end
416
- ```
417
-
418
410
  ###The Complete Ruby Code < (less then) 75 lines
419
411
 
420
412
  This is our complete `mychat.rb` Ruby application code:
@@ -430,7 +422,7 @@ class ChatController
430
422
  response['content-type'] = 'text/html'
431
423
  render(:chat)
432
424
  end
433
- def people
425
+ def_special_method "people.txt" do
434
426
  "I wrote this app :)"
435
427
  end
436
428
  def on_message data
@@ -441,40 +433,43 @@ class ChatController
441
433
  response.close
442
434
  return false
443
435
  end
444
- broadcast :_send_message, {event: :chat, from: params[:id], message: data["message"], at: Time.now}.to_json
436
+ broadcast :_send_message, {event: :chat, from: params[:id], message: ERB::Util.html_escape(data['message']), at: Time.now}.to_json
445
437
  end
446
438
  def _send_message data
447
439
  response << data
448
440
  end
449
- def on_connect
441
+ def on_open
450
442
  if params[:id].nil?
451
- response << {event: :error, from: :system, at: Time.now, message: "Error: cannot connect without a nickname!"}.to_json
443
+ response << {event: :error, from: :system, at: Time.now, message: "Error: cannot connect without a nickname!"}.to_json
452
444
  response.close
453
445
  return false
454
446
  end
455
447
  message = {from: '', at: Time.now}
456
- list = collect(:_ask_nickname)
457
- if ((list.map {|n| n.downcase}) + ['admin', 'system', 'sys', 'administrator']).include? params[:id].downcase
448
+ name = params[:id]
449
+ if (name.match(/admin|admn|system|sys|administrator/i))
458
450
  message[:event] = :error
459
- message[:message] = "The nickname '#{params[:id]}' is already taken."
451
+ message[:message] = "The nickname '#{name}' is already taken."
460
452
  response << message.to_json
461
453
  params[:id] = false
462
454
  response.close
463
455
  return
464
456
  end
465
457
  message[:event] = :chat
466
- message[:message] = list.empty? ? "You're the first one here." : "#{list[0..-2].join(', ')} #{list[1] ? 'and' : ''} #{list.last} #{list[1] ? 'are' : 'is'} already in the chatroom"
458
+ message[:message] = "Welcome #{params[:id]}."
459
+ # Should you end up storing your connected user names inside a manged list
460
+ # in redis or a database and then read that into a variable called 'list'
461
+ # here is some code you can use to write a message to the user based on the
462
+ # people currently in that list.
463
+ # message[:message] = list.empty? ? "You're the first one here." : "#{list[0..-2].join(', ')} #{list[1] ? 'and' : ''} #{list.last} #{list[1] ? 'are' : 'is'} already in the chatroom"
467
464
  response << message.to_json
468
- message[:message] = "#{params[:id]} joined the chatroom."
465
+ message[:message] = "#{name} joined the chatroom."
469
466
  broadcast :_send_message, message.to_json
470
467
  end
471
468
 
472
- def on_disconnect
469
+
470
+ def on_close
473
471
  broadcast :_send_message, {event: :chat, from: '', at: Time.now, message: "#{params[:id]} left the chatroom."}.to_json if params[:id]
474
472
  end
475
- def _ask_nickname
476
- return params[:id]
477
- end
478
473
  end
479
474
 
480
475
  # Using pathname extentions for setting public folder
@@ -494,7 +489,7 @@ service_options = {
494
489
  listen service_options
495
490
 
496
491
  # this routes the root of the application ('/') to our ChatController
497
- route '/', ChatController
492
+ route '/:id', ChatController
498
493
  ```
499
494
 
500
495
  ##The HTML - a web page with websockets
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: plezi
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.11.0
4
+ version: 0.11.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Boaz Segev
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-09-19 00:00:00.000000000 Z
11
+ date: 2015-09-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: grhttp
@@ -83,6 +83,7 @@ files:
83
83
  - lib/plezi/common/defer.rb
84
84
  - lib/plezi/common/dsl.rb
85
85
  - lib/plezi/common/redis.rb
86
+ - lib/plezi/common/renderer.rb
86
87
  - lib/plezi/common/settings.rb
87
88
  - lib/plezi/handlers/controller_core.rb
88
89
  - lib/plezi/handlers/controller_magic.rb