plezi 0.10.11 → 0.10.12
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +11 -1
- data/README.md +35 -9
- data/bin/plezi +1 -0
- data/lib/plezi.rb +1 -0
- data/lib/plezi/common/api.rb +135 -0
- data/lib/plezi/common/dsl.rb +91 -253
- data/lib/plezi/handlers/http_router.rb +1 -1
- data/lib/plezi/helpers/magic_helpers.rb +19 -0
- data/lib/plezi/version.rb +1 -1
- data/plezi.gemspec +1 -1
- data/resources/Gemfile +5 -0
- data/resources/mini_app.rb +9 -6
- data/resources/slim_config.rb +11 -0
- data/resources/websockets.js +26 -4
- data/resources/welcome_page.html +1 -1
- data/test/console +18 -0
- metadata +8 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 160e5060bd9cf3f8ec6acf8caa7d672ae238f3c7
|
4
|
+
data.tar.gz: d2816045210ac66cec18b0c5f37060a9ec5ae271
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 59691c8ce5b334a0e1b7223aaeb4e0ae74f5f800e24e803e8ad6e8aeb9492f299c71c50c46ca740a8a1940c7c50e4a28f332365ff40853b9221864cb8e3b2769
|
7
|
+
data.tar.gz: f738389e77c3bdc471e6000db4bfd83f8cbb0133814074c0ba4cbc69c5ecae229a18904f5cd3a8ca32253c7266637563c356cda8ca1cb1f2db4936c09d1a3c15
|
data/CHANGELOG.md
CHANGED
@@ -2,13 +2,23 @@
|
|
2
2
|
|
3
3
|
***
|
4
4
|
|
5
|
+
Change log v.0.10.12
|
6
|
+
|
7
|
+
**BIG Feature**: Run both your existing Rack app and plezi on he same GRHttp server - augment your app with all of Plezi's amasing features (two frameworks in one).
|
8
|
+
|
9
|
+
**Updates** updates to the mini template, the testing, the core API code and many more minor updates.
|
10
|
+
|
11
|
+
**API published**: Most of the private API in the Plezi::Base::DSL module was just made public (moving the methods to the main Plezi namespace). Your app should work as before unless you used private method calls instead of Plezi's published API.
|
12
|
+
|
13
|
+
***
|
14
|
+
|
5
15
|
Change log v.0.10.11
|
6
16
|
|
7
17
|
**Feature**: added the mini-app template, for quick websocket oriented apps that are meant to be attached to other frameworks (use `$ plezi mini appname` or `$ plezi m appname`).
|
8
18
|
|
9
19
|
**Feature**: allow Regexp hosts in the `listen` and `host` methods.
|
10
20
|
|
11
|
-
**Fix**: An error in the
|
21
|
+
**Fix**: An error in the cache system was introduced when performing slight performance enhancements (two variable names were switched). The issue is now fixed.
|
12
22
|
|
13
23
|
**Fix**: Correctly handle multiple `listen` calls with the same port number.
|
14
24
|
|
data/README.md
CHANGED
@@ -1,16 +1,12 @@
|
|
1
|
-
# Plezi, The
|
1
|
+
# Plezi, The Ruby framework for realtime web-apps
|
2
2
|
[![Gem Version](https://badge.fury.io/rb/plezi.svg)](http://badge.fury.io/rb/plezi)
|
3
3
|
[![Inline docs](http://inch-ci.org/github/boazsegev/plezi.svg?branch=master)](http://www.rubydoc.info/github/boazsegev/plezi/master)
|
4
4
|
|
5
|
-
|
5
|
+
Plezi is an easy to use Ruby Websocket Framework, with full RESTful routing support and HTTP streaming support. It's name comes from the word "fun", or "pleasure", since Plezi is a pleasure to work with.
|
6
6
|
|
7
|
-
|
7
|
+
I Believe that Plezi is a wonderful backend solution for developing SPAs, both with it's real-time native Websocket API and with it's RESTful routes that work great for writing easy AJAX requests.
|
8
8
|
|
9
|
-
|
10
|
-
|
11
|
-
Plezi is an easy to use Ruby Websocket Framework, with full RESTful routing support and HTTP streaming support. It's name comes from the word "fun" in Haitian, since Plezi is really fun to work with and it keeps our code clean and streamlined.
|
12
|
-
|
13
|
-
Plezi can both augment an existing Rails/Sinatra app, by providing it with easy Websocket and Asynchronous Events support, as well as offer an alternative to a Rack/Rails/Sintra/Faye/EM-Websockets combo. It's also great as an alternative to socket.io, allowing for both websockets and long pulling.
|
9
|
+
Plezi can both provide a wonderful alternative to existing complex platforms (i.e. Rails/Sinatra/Faye/EM) and augment an existing Rails/Sinatra app, by providing it with easy Websocket and Asynchronous Events support. It's also great as an alternative to socket.io, allowing for both websockets and long pulling.
|
14
10
|
|
15
11
|
Plezi runs over the [GRHttp server](https://github.com/boazsegev/GRHttp), which is a pure Ruby HTTP and Websocket Generic Server build using [GReactor](https://github.com/boazsegev/GReactor) - a multi-threaded pure ruby alternative to EventMachine with basic process forking support (enjoy it, if your code is scaling ready).
|
16
12
|
|
@@ -129,7 +125,37 @@ method names starting with an underscore ('_') will NOT be made public by the ro
|
|
129
125
|
|
130
126
|
You already have an amazing WebApp, but now you want to add websocket broadcasting and unicasting support - Plezi makes connection your existing WebApp with your Plezi Websocket backend as easy as it gets.
|
131
127
|
|
132
|
-
|
128
|
+
|
129
|
+
There are two easy ways to augment your existing WebApp, depending on your needs and preferences:
|
130
|
+
|
131
|
+
1. Let Plezi and GRHttp run your application as a fallback position, defering to your application for anything Plezi doesn't handle (Plezi Websockets and routes will recieve priority).
|
132
|
+
|
133
|
+
2. Run Plezi on a seperate process/server and set up communication between the two apps.
|
134
|
+
|
135
|
+
### The super easy augmentation - run together
|
136
|
+
|
137
|
+
The easiest way to augment your existing application is to use GRHttp's Rack adapter to run your Rack app, while Plezi will use GRHttp's native features (such as Websockets and HTTP streaming).
|
138
|
+
|
139
|
+
You can eaither use your existing Plezi application or create a new mini plezi application inside your existing app folder using:
|
140
|
+
|
141
|
+
$ plezi mini appname
|
142
|
+
|
143
|
+
Next, add the `plezi` gem to your `Gemfile` and add the following line somewhere in your apps code:
|
144
|
+
|
145
|
+
```ruby
|
146
|
+
require './appname/appname.rb'
|
147
|
+
Plezi.start_rack
|
148
|
+
```
|
149
|
+
|
150
|
+
That's it! Now you can use the Plezi API and your existing application's API at the same time and they are both running on the same server.
|
151
|
+
|
152
|
+
Plezi's route have priority, so that your app can keep handling the 404 (not found) error page.
|
153
|
+
|
154
|
+
In the next section we will explore how to set up the placebo API for cross process apps... since you are sharing the same space, you won't need it - but you can still use most of the Placebo API if you wish to do so (just **don't** call `Plezi.start_placebo`)
|
155
|
+
|
156
|
+
### The easy (but not super easy) augmentation - talking from afar
|
157
|
+
|
158
|
+
To use Plezi and your App on different processes, without mixing them together, simply include the Plezi App in your existing app and call `Plezi.start_placebo` - now you can access all the websocket API that you want from your existing WebApp.
|
133
159
|
|
134
160
|
For instance, add the following code to your environment on a Rails or Sinatra app:
|
135
161
|
|
data/bin/plezi
CHANGED
@@ -99,6 +99,7 @@ class AppTemplate
|
|
99
99
|
app_tree["config"]["sequel.rb"] ||= IO.read(::File.expand_path(File.join("..", "..", "resources" ,"db_sequel_config.rb"), __FILE__))
|
100
100
|
app_tree["config"]["datamapper.rb"] ||= IO.read(::File.expand_path(File.join("..", "..", "resources" ,"db_dm_config.rb"), __FILE__))
|
101
101
|
app_tree["config"]["haml.rb"] ||= IO.read(::File.expand_path(File.join("..", "..", "resources" ,"haml_config.rb"), __FILE__))
|
102
|
+
app_tree["config"]["slim.rb"] ||= IO.read(::File.expand_path(File.join("..", "..", "resources" ,"slim_config.rb"), __FILE__))
|
102
103
|
app_tree["config"]["i18n.rb"] ||= IO.read(::File.expand_path(File.join("..", "..", "resources" ,"i18n_config.rb"), __FILE__))
|
103
104
|
app_tree["config"]["redis.rb"] ||= (IO.read(::File.expand_path(File.join("..", "..", "resources" ,"redis_config.rb"), __FILE__))).gsub('appsecret', "#{ARGV[1]}_#{SecureRandom.hex}")
|
104
105
|
|
data/lib/plezi.rb
CHANGED
@@ -0,0 +1,135 @@
|
|
1
|
+
|
2
|
+
module Plezi
|
3
|
+
|
4
|
+
module_function
|
5
|
+
|
6
|
+
# Defines methods used to set up the Plezi app.
|
7
|
+
|
8
|
+
# public API to add a service to the framework.
|
9
|
+
# accepts a Hash object with any of the following options (Hash keys):
|
10
|
+
# port:: port number. defaults to 3000 or the port specified when the script was called.
|
11
|
+
# host:: the host name. defaults to any host not explicitly defined (a catch-all). NOTICE: in order to allow for hostname aliases, this is host emulation and the listening socket will bind to all the addresses available. To limit the actual binding use the `:bind` parameter as set by the GReactor's API - in which case host aliases might not work.
|
12
|
+
# alias:: a String or an Array of Strings which represent alternative host names (i.e. `alias: ["admin.google.com", "admin.gmail.com"]`).
|
13
|
+
# root:: the public root folder. if this is defined, static files will be served from the location.
|
14
|
+
# assets:: the assets root folder. defaults to nil (no assets support). if the path is defined, assets will be served from `/assets/...` (or the public_asset path defined) before any static files. assets will not be served if the file in the /public/assets folder if up to date (a rendering attempt will be made for systems that allow file writing).
|
15
|
+
# assets_public:: the assets public uri location (uri format, NOT a file path). defaults to `/assets`. assets will be saved (or rendered) to the assets public folder and served as static files.
|
16
|
+
# assets_callback:: a method that accepts two parameters: (request, response) and renders any custom assets. the method should return `false` unless it had set the response.
|
17
|
+
# save_assets:: saves the rendered assets to the filesystem, under the public folder. defaults to false.
|
18
|
+
# templates:: the templates root folder. defaults to nil (no template support). templates can be rendered by a Controller class, using the `render` method.
|
19
|
+
# ssl:: if true, an SSL service will be attempted. if no certificate is defined, an attempt will be made to create a self signed certificate.
|
20
|
+
# ssl_key:: the public key for the SSL service.
|
21
|
+
# ssl_cert:: the certificate for the SSL service.
|
22
|
+
#
|
23
|
+
# assets:
|
24
|
+
#
|
25
|
+
# assets support will render `.sass`, `.scss` and `.coffee` and save them as local files (`.css`, `.css`, and `.js` respectively)
|
26
|
+
# before sending them as static files.
|
27
|
+
#
|
28
|
+
# templates:
|
29
|
+
#
|
30
|
+
# ERB, Slim and Haml are natively supported. Otherwise define an assets_callback (or submit a pull request with a patch).
|
31
|
+
#
|
32
|
+
# @returns [Plezi::Router]
|
33
|
+
#
|
34
|
+
def listen parameters = {}
|
35
|
+
# update default values
|
36
|
+
parameters[:index_file] ||= 'index.html'
|
37
|
+
parameters[:assets_public] ||= '/assets'
|
38
|
+
parameters[:assets_public].chomp! '/'
|
39
|
+
|
40
|
+
# check if the port is used twice.
|
41
|
+
@routers_locker.synchronize do
|
42
|
+
@active_router = GRHttp.listen(parameters)
|
43
|
+
unless @active_router[:upgrade_handler]
|
44
|
+
@routers << (@active_router[:http_handler] = ::Plezi::Base::HTTPRouter.new)
|
45
|
+
@active_router[:upgrade_handler] = @active_router[:http_handler].upgrade_proc
|
46
|
+
else
|
47
|
+
@active_router.delete :alias
|
48
|
+
end
|
49
|
+
@active_router[:http_handler].add_host(parameters[:host], @active_router.merge(parameters) )
|
50
|
+
@active_router = @active_router[:http_handler]
|
51
|
+
end
|
52
|
+
# return the current handler or the protocol..
|
53
|
+
@active_router
|
54
|
+
end
|
55
|
+
|
56
|
+
# clears all the listeners and routes defined
|
57
|
+
def clear_app
|
58
|
+
@routers_locker.synchronize {GReactor.clear_listeners; @routers.clear}
|
59
|
+
end
|
60
|
+
# adds a route to the last server created
|
61
|
+
def route(path, controller = nil, &block)
|
62
|
+
raise "Must define a listener before adding a route - use `Plezi.listen`." unless @active_router
|
63
|
+
@routers_locker.synchronize { @active_router.add_route path, controller, &block }
|
64
|
+
end
|
65
|
+
|
66
|
+
|
67
|
+
# adds a shared route to all existing services and hosts.
|
68
|
+
def shared_route(path, controller = nil, &block)
|
69
|
+
raise "Must have created at least one Pleze service before calling `shared_route` - use `Plezi.listen`." unless @routers
|
70
|
+
@routers_locker.synchronize { @routers.each {|r| r.add_shared_route path, controller, &block } }
|
71
|
+
end
|
72
|
+
|
73
|
+
# adds a host to the last server created
|
74
|
+
#
|
75
|
+
# accepts a host name and a parameter(s) Hash which are the same parameter(s) as {Plezi.listen} accepts:
|
76
|
+
def host(host_name, params)
|
77
|
+
raise "Must define a listener before adding a route - use `Plezi.listen`." unless @active_router
|
78
|
+
@routers_locker.synchronize { @active_router.add_host host_name, params }
|
79
|
+
end
|
80
|
+
|
81
|
+
# starts the Plezi framework server and hangs until the exit signal is given.
|
82
|
+
def start
|
83
|
+
start_async
|
84
|
+
puts "\nPress ^C to exit.\n"
|
85
|
+
GReactor.join { puts "\r\nStarting shutdown sequesnce. Press ^C to force quit."}
|
86
|
+
end
|
87
|
+
|
88
|
+
# Makes sure the GRHttp server will be used by Rack (if Rack is available) and disables Plezi's autostart feature.
|
89
|
+
#
|
90
|
+
# This method is a both a fail safe and a shortcut. Plezi will automatically attempt to diable autostart when discovering Rack
|
91
|
+
# but this method also makes sure that the GRHttp is set as the Rack server by setting the ENV\["RACK_HANDLER"] variable.
|
92
|
+
#
|
93
|
+
# This is used as an alternative to {Plezi.start_placebo}.
|
94
|
+
#
|
95
|
+
# Use {Plezi.start_placebo} to augment an existing app while operating Plezi on a different process or server.
|
96
|
+
#
|
97
|
+
# Use {Plezi.start_rack} to augment an existing Rack app (i.e. Rails/Sinatra) by loading both Plezi and the existing Rack app
|
98
|
+
# to the GRHtto server (it will set up GRHttp as the Rack server).
|
99
|
+
def start_rack
|
100
|
+
Object.const_set("NO_PLEZI_AUTO_START", true) unless defined?(NO_PLEZI_AUTO_START)
|
101
|
+
ENV["RACK_HANDLER"] = 'grhttp'
|
102
|
+
end
|
103
|
+
# starts the Plezi framework and returns immidiately,
|
104
|
+
# allowing you to run the Plezi framework along side another framework.
|
105
|
+
def start_async
|
106
|
+
Object.const_set("NO_PLEZI_AUTO_START", true) unless defined?(NO_PLEZI_AUTO_START)
|
107
|
+
return GReactor.start if GReactor.running?
|
108
|
+
puts "Starting Plezi #{Plezi::VERSION} Services using the GRHttp #{GRHttp::VERSION} server."
|
109
|
+
GReactor.on_shutdown { puts "Plezi shutdown. It was fun to serve you." }
|
110
|
+
GReactor.start Plezi::Settings.max_threads
|
111
|
+
end
|
112
|
+
# This allows you to run the Plezi framework along side another framework - WITHOUT running the actual server.
|
113
|
+
#
|
114
|
+
# The server will not be initiatet and instead you will be able to use Plezi controllers and the Redis auto-config
|
115
|
+
# to broadcast Plezi messages to other Plezi processes - allowing for scalable intigration of Plezi into other frameworks.
|
116
|
+
def start_placebo
|
117
|
+
GReactor.clear_listeners
|
118
|
+
redis_connection # make sure the redis connection is activated
|
119
|
+
puts "* Plezi #{Plezi::VERSION} Services will start with no Server...\n"
|
120
|
+
start_async
|
121
|
+
end
|
122
|
+
|
123
|
+
# this module contains the methods that are used as a DSL and sets up easy access to the Plezi framework.
|
124
|
+
#
|
125
|
+
# use the`listen`, `host` and `route` functions rather then accessing this object.
|
126
|
+
#
|
127
|
+
@active_router = nil
|
128
|
+
@routers_locker = Mutex.new
|
129
|
+
@routers ||= [].to_set
|
130
|
+
end
|
131
|
+
|
132
|
+
Encoding.default_internal = 'utf-8'
|
133
|
+
Encoding.default_external = 'utf-8'
|
134
|
+
|
135
|
+
NO_PLEZI_AUTO_START = true if defined?(::Rack)
|
data/lib/plezi/common/dsl.rb
CHANGED
@@ -1,268 +1,106 @@
|
|
1
|
+
# PL is a shortcut for the Plezi module, so that `PL == Plezi`.
|
2
|
+
PL = Plezi
|
1
3
|
|
2
|
-
|
3
|
-
|
4
|
-
# this isn't part of the public API.
|
5
|
-
module Base
|
6
|
-
|
7
|
-
# holds methods that are called by the DSL.
|
8
|
-
#
|
9
|
-
# this isn't part of the public API.
|
10
|
-
module DSL
|
11
|
-
module_function
|
12
|
-
|
13
|
-
# this module contains the methods that are used as a DSL and sets up easy access to the Plezi framework.
|
14
|
-
#
|
15
|
-
# use the`listen`, `host` and `route` functions rather then accessing this object.
|
16
|
-
#
|
17
|
-
@active_router = nil
|
18
|
-
|
19
|
-
|
20
|
-
# public API to add a service to the framework.
|
21
|
-
# accepts a Hash object with any of the following options (Hash keys):
|
22
|
-
# port:: port number. defaults to 3000 or the port specified when the script was called.
|
23
|
-
# host:: the host name. defaults to any host not explicitly defined (a catch-all).
|
24
|
-
# alias:: a String or an Array of Strings which represent alternative host names (i.e. `alias: ["admin.google.com", "admin.gmail.com"]`).
|
25
|
-
# root:: the public root folder. if this is defined, static files will be served from the location.
|
26
|
-
# assets:: the assets root folder. defaults to nil (no assets support). if the path is defined, assets will be served from `/assets/...` (or the public_asset path defined) before any static files. assets will not be served if the file in the /public/assets folder if up to date (a rendering attempt will be made for systems that allow file writing).
|
27
|
-
# assets_public:: the assets public uri location (uri format, NOT a file path). defaults to `/assets`. assets will be saved (or rendered) to the assets public folder and served as static files.
|
28
|
-
# assets_callback:: a method that accepts two parameters: (request, response) and renders any custom assets. the method should return `false` unless it had set the response.
|
29
|
-
# save_assets:: saves the rendered assets to the filesystem, under the public folder. defaults to false.
|
30
|
-
# templates:: the templates root folder. defaults to nil (no template support). templates can be rendered by a Controller class, using the `render` method.
|
31
|
-
# ssl:: if true, an SSL service will be attempted. if no certificate is defined, an attempt will be made to create a self signed certificate.
|
32
|
-
# ssl_key:: the public key for the SSL service.
|
33
|
-
# ssl_cert:: the certificate for the SSL service.
|
34
|
-
#
|
35
|
-
# assets:
|
36
|
-
#
|
37
|
-
# assets support will render `.sass`, `.scss` and `.coffee` and save them as local files (`.css`, `.css`, and `.js` respectively)
|
38
|
-
# before sending them as static files.
|
39
|
-
#
|
40
|
-
# templates:
|
41
|
-
#
|
42
|
-
# ERB, Slim and Haml are natively supported.
|
43
|
-
#
|
44
|
-
# @returns [Plezi::Router]
|
45
|
-
#
|
46
|
-
def listen parameters = {}
|
47
|
-
# update default values
|
48
|
-
parameters[:index_file] ||= 'index.html'
|
49
|
-
parameters[:assets_public] ||= '/assets'
|
50
|
-
parameters[:assets_public].chomp! '/'
|
51
|
-
|
52
|
-
if !parameters[:port] && defined? ARGV
|
53
|
-
if ARGV.find_index('-p')
|
54
|
-
port_index = ARGV.find_index('-p') + 1
|
55
|
-
parameters[:port] ||= ARGV[port_index].to_i
|
56
|
-
ARGV[port_index] = (parameters[:port] + 1).to_s
|
57
|
-
else
|
58
|
-
ARGV << '-p'
|
59
|
-
ARGV << '3001'
|
60
|
-
parameters[:port] ||= 3000
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
#keeps information of past ports.
|
65
|
-
@listeners ||= {}
|
66
|
-
@listeners_locker = Mutex.new
|
67
|
-
|
68
|
-
# check if the port is used twice.
|
69
|
-
@listeners_locker.synchronize do
|
70
|
-
if @listeners[parameters[:port]]
|
71
|
-
puts "WARNING: port aleady in use! returning existing service and attemptin to add host (maybe multiple hosts? use `host` instead)."
|
72
|
-
@active_router = @listeners[parameters[:port]][:http_handler]
|
73
|
-
@active_router.add_host parameters[:host], parameters if @active_router.is_a?(HTTPRouter)
|
74
|
-
return @active_router
|
75
|
-
end
|
76
|
-
end
|
77
|
-
@listeners[parameters[:port]] = parameters
|
78
|
-
|
79
|
-
# make sure the protocol exists.
|
80
|
-
parameters[:http_handler] = HTTPRouter.new
|
81
|
-
parameters[:upgrade_handler] = parameters[:http_handler].upgrade_proc
|
82
|
-
|
83
|
-
GRHttp.listen parameters
|
84
|
-
# set the active router to the handler or the protocol.
|
85
|
-
@active_router = parameters[:http_handler]
|
86
|
-
@active_router.add_host(parameters[:host], parameters)
|
87
|
-
|
88
|
-
# return the current handler or the protocol..
|
89
|
-
@active_router
|
90
|
-
end
|
91
|
-
# adds a route to the last server created
|
92
|
-
def route(path, controller = nil, &block)
|
93
|
-
raise "Must define a listener before adding a route - use `Plezi.listen`." unless @active_router
|
94
|
-
@active_router.add_route path, controller, &block
|
95
|
-
end
|
96
|
-
|
97
|
-
|
98
|
-
# adds a shared route to all existing services and hosts.
|
99
|
-
def shared_route(path, controller = nil, &block)
|
100
|
-
raise "Must have created at least one Pleze service before calling `shared_route` - use `Plezi.listen`." unless @listeners
|
101
|
-
@listeners.values.each {|p| p[:http_handler].add_shared_route path, controller, &block }
|
102
|
-
end
|
103
|
-
|
104
|
-
# adds a host to the last server created
|
105
|
-
#
|
106
|
-
# accepts a host name and a parameter(s) Hash which are the same parameter(s) as {Plezi.listen} accepts:
|
107
|
-
def host(host_name, params)
|
108
|
-
raise "Must define a listener before adding a route - use `Plezi.listen`." unless @active_router
|
109
|
-
@active_router.add_host host_name, params
|
110
|
-
end
|
111
|
-
|
4
|
+
unless defined? PLEZI_NON_DSL
|
112
5
|
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
hs[k.to_s]
|
118
|
-
elsif k.is_a?(String) && hs.has_key?( k.to_sym)
|
119
|
-
hs[k.to_sym]
|
120
|
-
elsif k.is_a?(Numeric) && hs.has_key?(k.to_s.to_sym)
|
121
|
-
hs[k.to_s.to_sym]
|
122
|
-
end
|
123
|
-
end
|
124
|
-
hash.default_proc = @magic_hash_proc
|
125
|
-
hash.values.each do |v|
|
126
|
-
if v.is_a?(Hash)
|
127
|
-
make_hash_accept_symbols v
|
128
|
-
end
|
129
|
-
end
|
130
|
-
end
|
131
|
-
end
|
6
|
+
# shortcut for Plezi.listen.
|
7
|
+
#
|
8
|
+
def listen(params = {})
|
9
|
+
Plezi.listen params
|
132
10
|
end
|
133
11
|
|
134
|
-
#
|
135
|
-
def self.start
|
136
|
-
start_async
|
137
|
-
puts "\nPress ^C to exit.\n"
|
138
|
-
GReactor.join { puts "\r\nStarting shutdown sequesnce. Press ^C to force quit."}
|
139
|
-
end
|
140
|
-
# starts the Plezi framework and returns immidiately,
|
141
|
-
# allowing you to run the Plezi framework along side another framework.
|
142
|
-
def self.start_async
|
143
|
-
Object.const_set("NO_PLEZI_AUTO_START", true) unless defined?(NO_PLEZI_AUTO_START)
|
144
|
-
return GReactor.start if GReactor.running?
|
145
|
-
puts "Starting Plezi #{Plezi::VERSION} Services using the GRHttp #{GRHttp::VERSION} server."
|
146
|
-
GReactor.on_shutdown { puts "Plezi shutdown. It was fun to serve you." }
|
147
|
-
GReactor.start Plezi::Settings.max_threads
|
148
|
-
end
|
149
|
-
# This allows you to run the Plezi framework along side another framework - WITHOUT running the actual server.
|
12
|
+
# adds a virtul host to the current service (the last `listen` call) or switches to an existing host within the active service.
|
150
13
|
#
|
151
|
-
#
|
152
|
-
#
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
puts "* Plezi #{Plezi::VERSION} Services will start with no Server...\n"
|
157
|
-
start_async
|
14
|
+
# accepts:
|
15
|
+
# host_name: a String with the full host name (i.e. "www.google.com" / "mail.google.com")
|
16
|
+
# params:: any of the parameters accepted by the `listen` command, except `protocol`, `handler`, and `ssl` parameters.
|
17
|
+
def host(host_name = false, params = {})
|
18
|
+
Plezi.host host_name, params
|
158
19
|
end
|
159
|
-
end
|
160
20
|
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
#
|
165
|
-
|
166
|
-
|
167
|
-
#
|
168
|
-
#
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
#
|
174
|
-
#
|
175
|
-
#
|
176
|
-
#
|
177
|
-
#
|
178
|
-
|
179
|
-
|
180
|
-
|
21
|
+
# adds a route to the last server object
|
22
|
+
#
|
23
|
+
# path:: the path for the route
|
24
|
+
# controller:: The controller class which will accept the route.
|
25
|
+
#
|
26
|
+
# `path` parameters has a few options:
|
27
|
+
#
|
28
|
+
# * `path` can be a Regexp object, forcing the all the logic into controller (typically using the before method).
|
29
|
+
#
|
30
|
+
# * simple String paths are assumed to be basic RESTful paths:
|
31
|
+
#
|
32
|
+
# route "/users", Controller => route "/users/(:id)", Controller
|
33
|
+
#
|
34
|
+
# * routes can define their own parameters, for their own logic:
|
35
|
+
#
|
36
|
+
# route "/path/:required_paramater/:required_paramater{with_format}/(:optional_paramater)/(:optional){with_format}"
|
37
|
+
#
|
38
|
+
# * routes can define optional or required routes with regular expressions in them:
|
39
|
+
#
|
40
|
+
# route "(:locale){en|ru}/path"
|
41
|
+
#
|
42
|
+
# * routes which use the special '/' charecter within a parameter's format, must escape this charecter using the '\' charecter. **Notice the single quotes** in the following example:
|
43
|
+
#
|
44
|
+
# route '(:math){[\d\+\-\*\^\%\.\/]}'
|
45
|
+
#
|
46
|
+
# * or, with double quotes:
|
47
|
+
#
|
48
|
+
# route "(:math){[\\d\\+\\-\\*\\^\\%\\.\\/]}"
|
49
|
+
#
|
50
|
+
# magic routes make for difficult debugging - the smarter the routes, the more difficult the debugging.
|
51
|
+
# use with care and avoid complex routes when possible. RESTful routes are recommended when possible.
|
52
|
+
# json serving apps are advised to use required parameters, empty sections indicating missing required parameters (i.e. /path///foo/bar///).
|
53
|
+
#
|
54
|
+
def route(path, controller = nil, &block)
|
55
|
+
Plezi.route(path, controller, &block)
|
56
|
+
end
|
181
57
|
|
182
|
-
# adds a route to the
|
183
|
-
#
|
184
|
-
#
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
#
|
189
|
-
# * `path` can be a Regexp object, forcing the all the logic into controller (typically using the before method).
|
190
|
-
#
|
191
|
-
# * simple String paths are assumed to be basic RESTful paths:
|
192
|
-
#
|
193
|
-
# route "/users", Controller => route "/users/(:id)", Controller
|
194
|
-
#
|
195
|
-
# * routes can define their own parameters, for their own logic:
|
196
|
-
#
|
197
|
-
# route "/path/:required_paramater/:required_paramater{with_format}/(:optional_paramater)/(:optional){with_format}"
|
198
|
-
#
|
199
|
-
# * routes can define optional or required routes with regular expressions in them:
|
200
|
-
#
|
201
|
-
# route "(:locale){en|ru}/path"
|
202
|
-
#
|
203
|
-
# * routes which use the special '/' charecter within a parameter's format, must escape this charecter using the '\' charecter. **Notice the single quotes** in the following example:
|
204
|
-
#
|
205
|
-
# route '(:math){[\d\+\-\*\^\%\.\/]}'
|
206
|
-
#
|
207
|
-
# * or, with double quotes:
|
208
|
-
#
|
209
|
-
# route "(:math){[\\d\\+\\-\\*\\^\\%\\.\\/]}"
|
210
|
-
#
|
211
|
-
# magic routes make for difficult debugging - the smarter the routes, the more difficult the debugging.
|
212
|
-
# use with care and avoid complex routes when possible. RESTful routes are recommended when possible.
|
213
|
-
# json serving apps are advised to use required parameters, empty sections indicating missing required parameters (i.e. /path///foo/bar///).
|
214
|
-
#
|
215
|
-
def route(path, controller = nil, &block)
|
216
|
-
Plezi::Base::DSL.route(path, controller, &block)
|
217
|
-
end
|
58
|
+
# adds a route to the all the existing servers and hosts.
|
59
|
+
#
|
60
|
+
# accepts same options as route.
|
61
|
+
def shared_route(path, controller = nil, &block)
|
62
|
+
Plezi.shared_route(path, controller, &block)
|
63
|
+
end
|
218
64
|
|
219
|
-
#
|
220
|
-
#
|
221
|
-
#
|
222
|
-
|
223
|
-
|
224
|
-
|
65
|
+
# defines a method with a special name, such as "humens.txt".
|
66
|
+
#
|
67
|
+
# this could be used in controller classes, to define special routes which might defy
|
68
|
+
# normal Ruby naming conventions, such as "/welcome-home", "/play!", etc'
|
69
|
+
#
|
70
|
+
# could also be used to define methods with special formatting, such as "humans.txt",
|
71
|
+
# until a more refined way to deal with formatting will be implemented.
|
72
|
+
def def_special_method name, obj=self, &block
|
73
|
+
obj.instance_exec { define_method name.to_s.to_sym, &block }
|
74
|
+
end
|
225
75
|
|
226
|
-
# defines a method with a special name, such as "humens.txt".
|
227
|
-
#
|
228
|
-
# this could be used in controller classes, to define special routes which might defy
|
229
|
-
# normal Ruby naming conventions, such as "/welcome-home", "/play!", etc'
|
230
|
-
#
|
231
|
-
# could also be used to define methods with special formatting, such as "humans.txt",
|
232
|
-
# until a more refined way to deal with formatting will be implemented.
|
233
|
-
def def_special_method name, obj=self, &block
|
234
|
-
obj.instance_exec { define_method name.to_s.to_sym, &block }
|
235
|
-
end
|
236
76
|
|
237
77
|
|
78
|
+
# finishes setup of the servers and starts them up. This will hange the proceess.
|
79
|
+
#
|
80
|
+
# this method is called automatically by the Plezi framework.
|
81
|
+
#
|
82
|
+
# it is recommended that you DO NOT CALL this method.
|
83
|
+
# if any post shut-down actions need to be performed, use Plezi.on_shutdown instead.
|
84
|
+
def start_services
|
85
|
+
return 0 if defined?(NO_PLEZI_AUTO_START)
|
86
|
+
undef listen
|
87
|
+
undef host
|
88
|
+
undef route
|
89
|
+
undef shared_route
|
90
|
+
Plezi.start
|
91
|
+
end
|
238
92
|
|
239
|
-
#
|
240
|
-
|
241
|
-
|
242
|
-
#
|
243
|
-
#
|
244
|
-
#
|
245
|
-
def
|
246
|
-
|
247
|
-
|
248
|
-
undef host
|
249
|
-
undef route
|
250
|
-
undef shared_route
|
251
|
-
Plezi.start
|
252
|
-
end
|
93
|
+
# sets information to be used when restarting
|
94
|
+
$PL_SCRIPT = $0
|
95
|
+
$PL_ARGV = $*.dup
|
96
|
+
# restarts the Plezi app with the same arguments as when it was started.
|
97
|
+
#
|
98
|
+
# EXPERIMENTAL
|
99
|
+
def restart_plezi_app
|
100
|
+
exec "/usr/bin/env ruby #{$PL_SCRIPT} #{$PL_ARGV.join ' '}"
|
101
|
+
end
|
253
102
|
|
254
|
-
#
|
255
|
-
|
256
|
-
|
257
|
-
def restart_plezi_app
|
258
|
-
exec "/usr/bin/env ruby #{$PL_SCRIPT} #{$PL_ARGV.join ' '}"
|
103
|
+
# sets to start the services once dsl script is finished loading.
|
104
|
+
at_exit { start_services }
|
105
|
+
GReactor::Settings.force_graceful = false
|
259
106
|
end
|
260
|
-
|
261
|
-
# sets to start the services once dsl script is finished loading.
|
262
|
-
at_exit { start_services }
|
263
|
-
GReactor::Settings.force_graceful = false
|
264
|
-
|
265
|
-
# sets information to be used when restarting
|
266
|
-
$PL_SCRIPT = $0
|
267
|
-
$PL_ARGV = $*.dup
|
268
|
-
# $0="Plezi (Ruby)"
|
@@ -78,7 +78,7 @@ module Plezi
|
|
78
78
|
# return if a route answered the request
|
79
79
|
host.routes.each {|r| a = r.on_request(request, response); return a if a}
|
80
80
|
#return error code or 404 not found
|
81
|
-
Base::HTTPSender.send_by_code request, response, 404
|
81
|
+
return Base::HTTPSender.send_by_code request, response, 404 unless request[:io].params[:http_handler] == ::GRHttp::Base::Rack
|
82
82
|
rescue => e
|
83
83
|
# return 500 internal server error.
|
84
84
|
GReactor.error e
|
@@ -10,6 +10,25 @@ module Plezi
|
|
10
10
|
#
|
11
11
|
# (key type agnostic search Hash proc)
|
12
12
|
HASH_SYM_PROC = Proc.new {|h,k| k = (Symbol === k ? k.to_s : k.to_s.to_sym); h[k] if h.has_key?(k) }
|
13
|
+
|
14
|
+
# tweeks a hash object to read both :symbols and strings (similar to Rails but without).
|
15
|
+
def make_hash_accept_symbols hash
|
16
|
+
@magic_hash_proc ||= Proc.new do |hs,k|
|
17
|
+
if k.is_a?(Symbol) && hs.has_key?( k.to_s)
|
18
|
+
hs[k.to_s]
|
19
|
+
elsif k.is_a?(String) && hs.has_key?( k.to_sym)
|
20
|
+
hs[k.to_sym]
|
21
|
+
elsif k.is_a?(Numeric) && hs.has_key?(k.to_s.to_sym)
|
22
|
+
hs[k.to_s.to_sym]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
hash.default_proc = @magic_hash_proc
|
26
|
+
hash.values.each do |v|
|
27
|
+
if v.is_a?(Hash)
|
28
|
+
make_hash_accept_symbols v
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
13
32
|
end
|
14
33
|
end
|
15
34
|
|
data/lib/plezi/version.rb
CHANGED
data/plezi.gemspec
CHANGED
@@ -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.
|
21
|
+
spec.add_dependency "grhttp", "~> 0.0.15"
|
22
22
|
spec.add_development_dependency "bundler", "~> 1.7"
|
23
23
|
spec.add_development_dependency "rake", "~> 10.0"
|
24
24
|
|
data/resources/Gemfile
CHANGED
data/resources/mini_app.rb
CHANGED
@@ -1,16 +1,19 @@
|
|
1
1
|
# encoding: UTF-8
|
2
2
|
|
3
3
|
## Set working directory, load gems and create logs
|
4
|
-
|
4
|
+
## Using pathname extentions for setting public folder
|
5
5
|
require 'pathname'
|
6
|
-
|
6
|
+
## Set up root object, it might be used by the environment and\or the plezi extension gems.
|
7
7
|
Root ||= Pathname.new(File.dirname(__FILE__)).expand_path
|
8
|
-
|
9
|
-
Dir.chdir Root.to_s
|
10
|
-
|
8
|
+
## make sure all file access and file loading is relative to the application's root folder
|
9
|
+
# Dir.chdir Root.to_s
|
10
|
+
|
11
|
+
## Commet the following in order to use use your original app's Gemfile.
|
12
|
+
## If this app is independant, use bundler to load gems (including the plezi gem).
|
11
13
|
require 'bundler'
|
12
14
|
Bundler.require(:default, ENV['ENV'].to_s.to_sym)
|
13
|
-
|
15
|
+
|
16
|
+
## Uncomment to create a log file
|
14
17
|
# GReactor.create_logger File.expand_path(Root.join('server.log').to_s)
|
15
18
|
|
16
19
|
## Options for Scaling the app (across processes or machines):
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
if defined?(Slim)
|
4
|
+
require 'rouge/plugins/redcarpet' if defined?(Redcarpet) && defined?(Rouge)
|
5
|
+
if defined?(Redcarpet::Render::HTML) && defined?(Rouge::Plugins::Redcarpet)
|
6
|
+
Slim::Embedded.options[:markdown] = {
|
7
|
+
fenced_code_blocks: true,
|
8
|
+
renderer: (Class.new(Redcarpet::Render::HTML) {include Rouge::Plugins::Redcarpet} ).new
|
9
|
+
}
|
10
|
+
end
|
11
|
+
end
|
data/resources/websockets.js
CHANGED
@@ -1,22 +1,43 @@
|
|
1
|
+
// Add this file to your html to add websocket support
|
2
|
+
|
1
3
|
// Your websocket URI should be an absolute path. The following sets the base URI.
|
2
|
-
|
3
|
-
|
4
|
-
ws_uri
|
4
|
+
// remember to update to the specific controller's path to your websocket URI.
|
5
|
+
var ws_controller_path = window.location.pathname; // change to '/controller/path'
|
6
|
+
var ws_uri = (window.location.protocol.match(/https/) ? 'wss' : 'ws') + '://' + window.location.hostname + (window.location.port == '' ? '' : (':' + window.location.port) ) + ws_controller_path
|
5
7
|
// websocket variable.
|
6
8
|
var websocket = NaN
|
9
|
+
// count failed attempts
|
10
|
+
var websocket_fail_count = 0
|
11
|
+
// to limit failed reconnection attempts, set this to a number.
|
12
|
+
var websocket_fail_limit = NaN
|
13
|
+
|
7
14
|
|
8
15
|
function init_websocket()
|
9
16
|
{
|
10
17
|
websocket = new WebSocket(ws_uri);
|
11
18
|
websocket.onopen = function(e) {
|
19
|
+
// reset the count.
|
20
|
+
websocket_fail_count = 0
|
12
21
|
// what do you want to do now?
|
13
22
|
};
|
14
23
|
|
15
24
|
websocket.onclose = function(e) {
|
25
|
+
// If the websocket repeatedly you probably want to reopen the websocket if it closes
|
26
|
+
if(!isNaN(websocket_fail_limit) && websocket_fail_count >= websocket_fail_limit) {
|
27
|
+
// What to do if we can't reconnect so many times?
|
28
|
+
return
|
29
|
+
};
|
16
30
|
// you probably want to reopen the websocket if it closes.
|
17
|
-
|
31
|
+
if(isNaN(websocket_fail_limit) || (websocket_fail_count <= websocket_fail_limit) ) {
|
32
|
+
// update the count
|
33
|
+
websocket_fail_count += 1;
|
34
|
+
// try to reconect
|
35
|
+
init_websocket();
|
36
|
+
};
|
18
37
|
};
|
19
38
|
websocket.onerror = function(e) {
|
39
|
+
// update the count.
|
40
|
+
websocket_fail_limit += 1
|
20
41
|
// what do you want to do now?
|
21
42
|
};
|
22
43
|
websocket.onmessage = function(e) {
|
@@ -26,4 +47,5 @@ function init_websocket()
|
|
26
47
|
// msg = JSON.parse(e.data); // remember to use JSON also in your Plezi controller.
|
27
48
|
};
|
28
49
|
}
|
50
|
+
// setup the websocket connection once the page is done loading
|
29
51
|
window.addEventListener("load", init_websocket, false);
|
data/resources/welcome_page.html
CHANGED
@@ -175,7 +175,7 @@ input[type=submit]:active
|
|
175
175
|
</style>
|
176
176
|
<script type="text/javascript">
|
177
177
|
// Your websocket URI should be an absolute path. The following sets the base URI.
|
178
|
-
var ws_uri = (window.location.protocol.match(/https/) ? 'wss' : 'ws') + '://' + window.location.hostname + (window.location.port == '' ? '' : (':' + window.location.port) );
|
178
|
+
var ws_uri = (window.location.protocol.match(/https/) ? 'wss' : 'ws') + '://' + window.location.hostname + (window.location.port == '' ? '' : (':' + window.location.port) ) + window.location.pathname;
|
179
179
|
// remember to add the specific controller's path to your websocket URI.
|
180
180
|
ws_uri += "/";
|
181
181
|
// websocket variable.
|
data/test/console
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# encoding: UTF-8
|
3
|
+
|
4
|
+
Dir.chdir '/Users/2Be/Ruby/plezi/plezi/'
|
5
|
+
|
6
|
+
require 'benchmark'
|
7
|
+
require "bundler/setup"
|
8
|
+
require "plezi"
|
9
|
+
|
10
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
11
|
+
# with your gem easier. You can also use a different console, if you like.
|
12
|
+
|
13
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
14
|
+
# require "pry"
|
15
|
+
# Pry.start
|
16
|
+
|
17
|
+
require "irb"
|
18
|
+
IRB.start
|
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.10.
|
4
|
+
version: 0.10.12
|
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-07-
|
11
|
+
date: 2015-07-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: grhttp
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 0.0.
|
19
|
+
version: 0.0.15
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 0.0.
|
26
|
+
version: 0.0.15
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: bundler
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -69,6 +69,7 @@ files:
|
|
69
69
|
- Rakefile
|
70
70
|
- bin/plezi
|
71
71
|
- lib/plezi.rb
|
72
|
+
- lib/plezi/common/api.rb
|
72
73
|
- lib/plezi/common/cache.rb
|
73
74
|
- lib/plezi/common/defer.rb
|
74
75
|
- lib/plezi/common/dsl.rb
|
@@ -114,8 +115,10 @@ files:
|
|
114
115
|
- resources/rakefile
|
115
116
|
- resources/redis_config.rb
|
116
117
|
- resources/routes.rb
|
118
|
+
- resources/slim_config.rb
|
117
119
|
- resources/websockets.js
|
118
120
|
- resources/welcome_page.html
|
121
|
+
- test/console
|
119
122
|
- test/plezi_tests.rb
|
120
123
|
- websocket chatroom.md
|
121
124
|
homepage: http://boazsegev.github.io/plezi/
|
@@ -145,4 +148,5 @@ specification_version: 4
|
|
145
148
|
summary: Plezi is the native Ruby Framework for real time web-apps. An easy way to
|
146
149
|
write Websockets, RESTful routing and HTTP streaming apps.
|
147
150
|
test_files:
|
151
|
+
- test/console
|
148
152
|
- test/plezi_tests.rb
|