plezi 0.8.3 → 0.8.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1e948ea3cceaa995b5dc7a9c496432e5ef1c0660
4
- data.tar.gz: 9c70d2dbb9291d65500daa109a751e6228785d50
3
+ metadata.gz: 5e19a46b1de10e66f183bb5e62328d68762725cd
4
+ data.tar.gz: 9e7169464ae76ac2fc86387d30d7203a093244c5
5
5
  SHA512:
6
- metadata.gz: 9ef50d068ce5fb0f806d7216daea2cd458b4aa5d93534ca56d2a15b7f2324e9638f1e7dbf3318accf957bd59f691b2bcfc45a82a3ea0db6e570d479cfa5af3da
7
- data.tar.gz: eb1ca84b326181a96bab242927640c1d4fc1663db4ce6f433b1d4d386dd7cc748fc923bf97c948a076c032212c9ddc41931f4254951969db828a8fd85dc36802
6
+ metadata.gz: 1591203078e67b70960fb20695962f3c2c7e01f5097a6272c1f7dac1d053641e943cbe808c31061c906910b42554333ed2d3f7342614172bb6036bd6031f7103
7
+ data.tar.gz: 0e5fdf7ca6e8fa261a827cec565ffaf3a1c44d526d346f84c664a9a2ee9a76ee2f473cf07274383509a91d0535a0daea6f6d7be1fac73e7802283e5bb49d98f8
data/CHANGELOG.md CHANGED
@@ -2,6 +2,18 @@
2
2
 
3
3
  ***
4
4
 
5
+ Change log v.0.8.4
6
+
7
+ **core routing changes**: moved the Controller's routing cache out of the global cahce store. This might provide a very slight performance increase.
8
+
9
+ **feature**: a new OAuth2 controller offers an easy support for OAuth2 login services such as facebook and google. to use this feature, require 'plezi/oauth'.
10
+
11
+ **fix**: fixed an issue where RESTful requests to `new` would be mistakenly routed to the `save` method.
12
+
13
+ **testing**: some basic testing for the RESTful Plezi framework has been implemented. Please notice that the tests WILL run the Plezi server on ports 3000 (for http) and 3030 (for https) during the test. The test will run Net::HTTP requests against the Plezi server.
14
+
15
+ ***
16
+
5
17
  Change log v.0.8.3
6
18
 
7
19
  **Auto-ping feature**: WebSocket connections now automatically send a `ping` every ~45 seconds (approximately) before the websocket's connection would timeout. This auto-ping will keep the connection alive even if no data is exchanged.
data/Rakefile CHANGED
@@ -1 +1,7 @@
1
1
  require "bundler/gem_tasks"
2
+
3
+ # create a default task
4
+ desc "Not yet implemented: Tests most of the basic features for the Plezi framework."
5
+ task :test do
6
+ load "./test/plezi_tests.rb"
7
+ end
data/bin/plezi CHANGED
@@ -61,12 +61,13 @@ class AppTemplate
61
61
 
62
62
  # set up config files
63
63
  app_tree["config"] ||= {}
64
- app_tree["config"]["db_ac_config.rb"] ||= IO.read(::File.expand_path(File.join("..", "..", "resources" ,"db_ac_config.rb"), __FILE__))
65
- app_tree["config"]["db_sequel_config.rb"] ||= IO.read(::File.expand_path(File.join("..", "..", "resources" ,"db_sequel_config.rb"), __FILE__))
66
- app_tree["config"]["db_dm_config.rb"] ||= IO.read(::File.expand_path(File.join("..", "..", "resources" ,"db_dm_config.rb"), __FILE__))
67
- app_tree["config"]["haml_config.rb"] ||= IO.read(::File.expand_path(File.join("..", "..", "resources" ,"haml_config.rb"), __FILE__))
68
- app_tree["config"]["i18n_config.rb"] ||= IO.read(::File.expand_path(File.join("..", "..", "resources" ,"i18n_config.rb"), __FILE__))
69
- app_tree["config"]["redis_config.rb"] ||= (IO.read(::File.expand_path(File.join("..", "..", "resources" ,"redis_config.rb"), __FILE__))).gsub('appsecret', "#{ARGV[1]}_#{SecureRandom.hex}")
64
+ app_tree["config"]["oauth.rb"] ||= IO.read(::File.expand_path(File.join("..", "..", "resources" ,"oauth_config.rb"), __FILE__))
65
+ app_tree["config"]["db_active_record.rb"] ||= IO.read(::File.expand_path(File.join("..", "..", "resources" ,"db_ac_config.rb"), __FILE__))
66
+ app_tree["config"]["db_sequel.rb"] ||= IO.read(::File.expand_path(File.join("..", "..", "resources" ,"db_sequel_config.rb"), __FILE__))
67
+ app_tree["config"]["db_datamapper.rb"] ||= IO.read(::File.expand_path(File.join("..", "..", "resources" ,"db_dm_config.rb"), __FILE__))
68
+ app_tree["config"]["haml.rb"] ||= IO.read(::File.expand_path(File.join("..", "..", "resources" ,"haml_config.rb"), __FILE__))
69
+ app_tree["config"]["i18n.rb"] ||= IO.read(::File.expand_path(File.join("..", "..", "resources" ,"i18n_config.rb"), __FILE__))
70
+ app_tree["config"]["redis.rb"] ||= (IO.read(::File.expand_path(File.join("..", "..", "resources" ,"redis_config.rb"), __FILE__))).gsub('appsecret', "#{ARGV[1]}_#{SecureRandom.hex}")
70
71
 
71
72
  #set up database stub folders
72
73
  app_tree["db"] ||= {}
@@ -198,7 +198,7 @@ module Plezi
198
198
  # respond to websocket special case
199
199
  return :pre_connect if request['upgrade'] && request['upgrade'].to_s.downcase == 'websocket' && request['connection'].to_s.downcase == 'upgrade'
200
200
  # respond to save 'new' special case
201
- return :save if request.request_method.match(/POST|PUT|PATCH/) && params[:id].nil? || params[:id] == 'new'
201
+ return :save if request.request_method.match(/POST|PUT|PATCH/) && (params[:id].nil? || params[:id] == 'new')
202
202
  # set DELETE method if simulated
203
203
  request.request_method = 'DELETE' if params[:_method].to_s.downcase == 'delete'
204
204
  # respond to special :id routing
@@ -279,19 +279,18 @@ module Plezi
279
279
  # lists the available methods that will be exposed to HTTP requests
280
280
  def available_public_methods
281
281
  # set class global to improve performance while checking for supported methods
282
- Plezi.cached?(self.superclass.name + '_p&rt') ? Plezi.get_cached(self.superclass.name + "_p&rt") : Plezi.cache_data(self.superclass.name + "_p&rt", (available_routing_methods - [:before, :after, :save, :show, :update, :delete, :initialize, :on_message, :pre_connect, :on_connect, :on_disconnect]).to_set )
282
+ @available_public_methods ||= (available_routing_methods - [:before, :after, :save, :show, :update, :delete, :initialize, :on_message, :pre_connect, :on_connect, :on_disconnect]).to_set
283
283
  end
284
284
 
285
285
  # lists the available methods that will be exposed to the HTTP router
286
286
  def available_routing_methods
287
- # set class global to improve performance while checking for supported methods
288
- Plezi.cached?(self.superclass.name + '_r&rt') ? Plezi.get_cached(self.superclass.name + "_r&rt") : Plezi.cache_data(self.superclass.name + "_r&rt", (((public_instance_methods - Object.public_instance_methods) - Plezi::ControllerMagic::InstanceMethods.instance_methods).delete_if {|m| m.to_s[0] == '_'}).to_set )
287
+ @available_routing_methods ||= ( ( (public_instance_methods - Object.public_instance_methods) - Plezi::ControllerMagic::InstanceMethods.instance_methods).delete_if {|m| m.to_s[0] == '_'}).to_set
289
288
  end
290
289
 
291
290
  # resets this controller's router, to allow for dynamic changes
292
291
  def reset_routing_cache
293
- Plezi.clear_cached(self.superclass.name + '_p&rt')
294
- Plezi.clear_cached(self.superclass.name + '_r&rt')
292
+ @available_routing_methods = false
293
+ @available_public_methods = false
295
294
  available_routing_methods
296
295
  available_public_methods
297
296
  end
@@ -308,6 +307,26 @@ module Plezi
308
307
  def method_undefined(id)
309
308
  reset_routing_cache
310
309
  end
310
+ # # lists the available methods that will be exposed to HTTP requests
311
+ # def available_public_methods
312
+ # # set class global to improve performance while checking for supported methods
313
+ # Plezi.cached?(self.superclass.name + '_p&rt') ? Plezi.get_cached(self.superclass.name + "_p&rt") : Plezi.cache_data(self.superclass.name + "_p&rt", (available_routing_methods - [:before, :after, :save, :show, :update, :delete, :initialize, :on_message, :pre_connect, :on_connect, :on_disconnect]).to_set )
314
+ # end
315
+
316
+ # # lists the available methods that will be exposed to the HTTP router
317
+ # def available_routing_methods
318
+ # # set class global to improve performance while checking for supported methods
319
+ # Plezi.cached?(self.superclass.name + '_r&rt') ? Plezi.get_cached(self.superclass.name + "_r&rt") : Plezi.cache_data(self.superclass.name + "_r&rt", (((public_instance_methods - Object.public_instance_methods) - Plezi::ControllerMagic::InstanceMethods.instance_methods).delete_if {|m| m.to_s[0] == '_'}).to_set )
320
+ # end
321
+
322
+ # # resets this controller's router, to allow for dynamic changes
323
+ # def reset_routing_cache
324
+ # Plezi.clear_cached(self.superclass.name + '_p&rt')
325
+ # Plezi.clear_cached(self.superclass.name + '_r&rt')
326
+ # available_routing_methods
327
+ # available_public_methods
328
+ # end
329
+
311
330
 
312
331
  # reviews the Redis connection, sets it up if it's missing and returns the Redis connection.
313
332
  #
@@ -318,10 +337,10 @@ module Plezi
318
337
  # ENV['PL_REDIS_URL'] = "redis://username:password@my.host:6379"
319
338
  def redis_connection
320
339
  # return false unless defined?(Redis) && ENV['PL_REDIS_URL']
321
- # return @@redis if defined?(@@redis_sub_thread) && @@redis
340
+ # return @redis if defined?(@redis_sub_thread) && @redis
322
341
  # @@redis_uri ||= URI.parse(ENV['PL_REDIS_URL'])
323
- # @@redis ||= Redis.new(host: @@redis_uri.host, port: @@redis_uri.port, password: @@redis_uri.password)
324
- # @@redis_sub_thread = Thread.new do
342
+ # @redis ||= Redis.new(host: @@redis_uri.host, port: @@redis_uri.port, password: @@redis_uri.password)
343
+ # @redis_sub_thread = Thread.new do
325
344
  # begin
326
345
  # Redis.new(host: @@redis_uri.host, port: @@redis_uri.port, password: @@redis_uri.password).subscribe(redis_channel_name) do |on|
327
346
  # on.message do |channel, msg|
@@ -335,8 +354,8 @@ module Plezi
335
354
  # retry
336
355
  # end
337
356
  # end
338
- # raise "Redis connction failed for: #{ENV['PL_REDIS_URL']}" unless @@redis
339
- # @@redis
357
+ # raise "Redis connction failed for: #{ENV['PL_REDIS_URL']}" unless @redis
358
+ # @redis
340
359
  return false unless defined?(Redis) && ENV['PL_REDIS_URL']
341
360
  return Plezi.get_cached(self.superclass.name + '_b') if Plezi.cached?(self.superclass.name + '_b')
342
361
  @@redis_uri ||= URI.parse(ENV['PL_REDIS_URL'])
@@ -0,0 +1,4 @@
1
+ # requires the open-uri library.
2
+ require 'open-uri'
3
+ # require the OAuth2 controller
4
+ require 'plezi/oauth/auth_controller.rb'
@@ -0,0 +1,208 @@
1
+ require 'open-uri'
2
+
3
+
4
+
5
+ module Plezi
6
+
7
+ #########################################
8
+ # This is a social Authentication Controller
9
+ #
10
+ # This controller currently supports:
11
+ #
12
+ # - Facebook authentication using the Graph API, v. 2.3 - [see Facebook documentation](https://developers.facebook.com/docs/facebook-login/manually-build-a-login-flow/v2.3).
13
+ # - Google authentication using the OAuth 2.0 API - [see Google documentation](https://developers.google.com/identity/protocols/OAuth2WebServer).
14
+ #
15
+ # It's also possible to manualy register any OAuth 2.0 authentication service using the `register_service` method.
16
+ #
17
+ # To use this Controller in your application, make sure the following variables are set:
18
+ # ENV['FB_APP_ID'] = {facebook_app_id}
19
+ # ENV['FB_APP_SECRET'] = {facebook_app_secret}
20
+ # ENV['GOOGLE_APP_ID'] = {google_app_id}
21
+ # ENV['GOOGLE_APP_SECRET'] = {google_app_secret}
22
+ #
23
+ # Add the following route to routes.rb file (as always, route placement order effects behavior):
24
+ #
25
+ # create_auth_shared_route do |service, remote_user_id, email, full_remote_response|
26
+ # # perform any specific app authentication logic such as saving the info.
27
+ # # return the current user or false if the callback is called with an authentication failure.
28
+ # end
29
+ #
30
+ # The `create_auth_shared_route` method is a shortcut for calling the `#shared_route` method with the relevant arguments and setting the OAuth2Ctrl callback.
31
+ #
32
+ # Use the following links for social authentication:
33
+ #
34
+ # - Facebook: "/auth/facebook?redirect_after=/foo/bar"
35
+ # - Google: "/auth/google?redirect_after=/foo/bar"
36
+ # - foo_service: "/auth/foo_service?redirect_after=/foo/bar"
37
+ #
38
+ class OAuth2Ctrl
39
+
40
+ # Sets (or gets) the callback to be called after authentication is attempeted.
41
+ #
42
+ # Accepts a block that will be called with the following parameters:
43
+ # service_name:: the name of the service. i.e. :facebook, :google, etc'.
44
+ # service_token:: the authentication token returned by the service. This token should be stored for future access
45
+ # remote_user_id:: service's user id.
46
+ # remote_user_email:: users primamry email, if registed with the service.
47
+ # remote_response:: a Hash with the complete user data response (including email and id).
48
+ #
49
+ # If the authentication fails for Facebook, the block will be called with the following values `auth_callback.call(nil, ni, {server: :responce, might_be: :empty})`
50
+ #
51
+ # The block will be run in the context of the controller and all the controller's methods will be available to it.
52
+ #
53
+ # i.e.:
54
+ # OAuth2Ctrl.auth_callback |service, service_token, id, email, res| cookies["#{service}_pl_auth_token".to_sym], cookies["#{service}_user_id".to_sym], cookies["#{service}_user_email".to_sym] = service_token, id, email }
55
+ #
56
+ # defaults to the example above, which isn't a very sercure behavior, but allows for easy testing.
57
+ def self.auth_callback &block
58
+ block_given? ? (@auth_callback = block) : ( @auth_callback ||= (Proc.new {|service, service_token, id, email, res| Plezi.info "deafult callback called for #{service}, with response: #{res.to_s}"; cookies["#{service}_pl_auth_token".to_sym], cookies["#{service}_user_id".to_sym], cookies["#{service}_user_email".to_sym] = service_token, id, email}) )
59
+ end
60
+
61
+
62
+ # Stores the registered services library
63
+ SERVICES = {}
64
+
65
+ # This method registers a social login service that conforms to the OAuth2 model.
66
+ #
67
+ # Accepts the following required parameters:
68
+ # service_name:: a Symbol naming the service. i.e. :facebook or :google .
69
+ # options:: a Hash of options, some of which are required.
70
+ #
71
+ # The options are:
72
+ # app_id:: the aplication's unique ID registered with the service. i.e. ENV[FB_APP_ID] (storing these in environment variables is safer then hardcoding them)
73
+ # app_secret:: the aplication's unique secret registered with the service.
74
+ # auth_url:: the authentication URL. This is the url to which the user is redirected. i.e.: "https://www.facebook.com/dialog/oauth"
75
+ # token_url:: the token request URL. This is the url used to switch the single-use code into a persistant authentication token. i.e.: "https://www.googleapis.com/oauth2/v3/token"
76
+ # profile_url:: the URL used to ask the service for the user's profile (the service's API url). i.e.: "https://graph.facebook.com/v2.3/me"
77
+ # scope:: a String representing the scope requested. i.e. 'email profile'.
78
+ #
79
+ # There will be an attempt to aitomatically register Facebook and Google login services under these conditions:
80
+ #
81
+ # * For Facebook: Both ENV['FB_APP_ID'] && ENV['FB_APP_SECRET'] have been defined.
82
+ # * For Google: Both ENV['GOOGLE_APP_ID'] && ENV['GOOGLE_APP_SECRET'] have been defined.
83
+ #
84
+ #
85
+ # Just for reference, at the time of this writing:
86
+ #
87
+ # * facebook auth_url: "https://www.facebook.com/dialog/oauth"
88
+ # * facebook token_url: "https://graph.facebook.com/v2.3/oauth/access_token"
89
+ # * facebook profile_url: "https://graph.facebook.com/v2.3/me"
90
+ # * google auth_url: "https://accounts.google.com/o/oauth2/auth"
91
+ # * google token_url: "https://www.googleapis.com/oauth2/v3/token"
92
+ # * google profile_url: "https://www.googleapis.com/plus/v1/people/me"
93
+ #
94
+ def self.register_service service_name, options
95
+ raise "Cannot register service, missing required information." unless service_name && options[:auth_url] && options[:token_url] && options[:profile_url] && options[:app_id] && options[:app_secret]
96
+ # for google, scope is space delimited. for facebook it's comma delimited
97
+ options[:scope] ||= 'profile, email'
98
+ SERVICES[service_name] = options
99
+ end
100
+
101
+ # Called to manually run through the authentication logic for the requested service,
102
+ # without performing any redirections.
103
+ #
104
+ # Use this method to attempt and re-login a user using an existing login token:
105
+ #
106
+ # token = 'google_token_could_be_recieved_also_from_javascript_sdk'
107
+ # OAuth2Ctrl.auth :google, token, self
108
+ #
109
+ # The method will return false if re-login fails and it will otherwise return the callback's return value.
110
+ #
111
+ # Call this method from within a controller, passing the controller (self) to the method, like so:
112
+ #
113
+ # OAuth2Ctrl.auth :facebook, token, self
114
+ #
115
+ # This is especially effective if `auth_callback` returns the user object, as it would allow to chain
116
+ # different login methods, i.e.:
117
+ #
118
+ # @user ||= app_login || OAuth2Ctrl.auth(:facebook, fb_token, self) || OAuth2Ctrl.auth(:google, google_token, self) || ....
119
+ def self.auth service_name, service_token, controller
120
+ service = SERVICES[service_name]
121
+ retrun false unless service
122
+ # auth_res = controller.cookies[c_name] ? (JSON.parse URI.parse("#{service[:profile_url]}?access_token=#{controller.cookies[c_name]}").read rescue ({}) ) : {}
123
+ # controller.cookies[c_name] = nil unless auth_res['id']
124
+ # auth_res['id'] ? controller.instance_exec( service_name, auth_res['id'], auth_res['email'], auth_res, &auth_callback) : ( controller.instance_exec( service_name, nil, nil, auth_res, &auth_callback) && false)
125
+ auth_res = {}
126
+ uri = URI.parse("#{service[:profile_url]}?access_token=#{service_token}")
127
+ Net::HTTP.start(uri.host, uri.port, use_ssl: (uri.scheme == "https"), verify_mode: OpenSSL::SSL::VERIFY_NONE) do |http|
128
+ req = Net::HTTP::Get.new(uri)
129
+ res = http.request(req).body
130
+ auth_res = (JSON.parse res) rescue ({})
131
+ end if service_token
132
+ auth_res['id'] ? controller.instance_exec( service_name, service_token, auth_res['id'], auth_res['email'], auth_res, &auth_callback) : ( controller.instance_exec( service_name, nil, nil, nil, auth_res, &auth_callback) && false)
133
+ end
134
+
135
+ def update
136
+ service_name = params[:id].to_s.to_sym
137
+ service = SERVICES[service_name]
138
+ retrun false unless service
139
+ if params[:error]
140
+ instance_exec( service_name, nil, nil, nil, {}, &self.class.auth_callback)
141
+ return redirect_to(flash[:redirect_after])
142
+ end
143
+ unless params[:code]
144
+ flash[:redirect_after] = params[:redirect_after] || '/'
145
+ return redirect_to _auth_url_for(service_name)
146
+ end
147
+ uri = URI.parse service[:token_url]
148
+ service_token = nil
149
+ Net::HTTP.start(uri.host, uri.port, use_ssl: (uri.scheme == "https"), verify_mode: OpenSSL::SSL::VERIFY_NONE) do |http|
150
+ req = Net::HTTP::Post.new(uri)
151
+ req.form_data = {"client_id" => service[:app_id],
152
+ "client_secret" => service[:app_secret], "code" => params[:code] ,
153
+ "grant_type" => "authorization_code","redirect_uri" => "#{request.base_url}/auth/#{service_name.to_s}"}
154
+ res = http.request(req).body
155
+ service_token = ((JSON.parse res)['access_token'] rescue nil)
156
+ end
157
+
158
+ self.class.auth service_name, service_token, self
159
+ redirect_to(flash[:redirect_after])
160
+ end
161
+ alias :show :update
162
+
163
+ protected
164
+
165
+ # returns a url for the requested service's social login.
166
+ #
167
+ # this is used internally. Normal behavior would be to set a link to '/auth/{service_name}', where correct redirection will be automatic.
168
+ #
169
+ def _auth_url_for service_name
170
+ service = SERVICES[service_name]
171
+ return nil unless service
172
+ redirect_uri = HTTP::encode "#{request.base_url}/auth/#{service_name.to_s}", :url #response_type
173
+ return "#{service[:auth_url]}?client_id=#{service[:app_id]}&redirect_uri=#{redirect_uri}&scope=#{service[:scope]}&response_type=code"
174
+ end
175
+
176
+ register_service(:facebook, app_id: ENV['FB_APP_ID'],
177
+ app_secret: ENV['FB_APP_SECRET'],
178
+ auth_url: "https://www.facebook.com/dialog/oauth",
179
+ token_url: "https://graph.facebook.com/v2.3/oauth/access_token",
180
+ profile_url: "https://graph.facebook.com/v2.3/me",
181
+ scope: "public_profile,email") if ENV['FB_APP_ID'] && ENV['FB_APP_SECRET']
182
+ register_service(:google, app_id: ENV['GOOGLE_APP_ID'],
183
+ app_secret: ENV['GOOGLE_APP_SECRET'],
184
+ auth_url: "https://accounts.google.com/o/oauth2/auth",
185
+ token_url: "https://www.googleapis.com/oauth2/v3/token",
186
+ profile_url: "https://www.googleapis.com/oauth2/v1/userinfo",
187
+ scope: "profile email") if ENV['GOOGLE_APP_ID'] && ENV['GOOGLE_APP_SECRET']
188
+
189
+ end
190
+ end
191
+
192
+ # This method creates the OAuth2Ctrl route.
193
+ # This is actually a short-cut for:
194
+ #
195
+ # shared_route "auth/(:id)/(:code)" , Plezi::OAuth2Ctrl
196
+ # Plezi::OAuth2Ctrl.auth_callback = Proc.new {|service, user_id, user_email, service_response| ... }
197
+ #
198
+ # the `:id` parameter is used to identify the service (facebook, google. etc').
199
+ #
200
+ # The method accepts a block that will be used to set the authentication callback. See the Plezi::OAuth2Ctrl documentation for details.
201
+ #
202
+ # The method can be called only once and will self-destruct.
203
+ def create_auth_shared_route options = {}, &block
204
+ shared_route "auth/(:id)/(:code)" , Plezi::OAuth2Ctrl
205
+ undef create_auth_shared_route
206
+ Plezi::OAuth2Ctrl.auth_callback = block if block
207
+ Plezi::OAuth2Ctrl
208
+ end
data/lib/plezi/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Plezi
2
- VERSION = "0.8.3"
2
+ VERSION = "0.8.4"
3
3
  end
@@ -0,0 +1,24 @@
1
+ ###############
2
+ # # OAuth2 Config File
3
+ # # ==================
4
+ # #
5
+ # # Here you can sets the OAuth2 variables and require the OAuth2 Plezi controller.
6
+ # # (if the variables aren't set BEFORE the inclution, automatic setup will NOT take place)
7
+ # #
8
+ # # First set the variables:
9
+ # #
10
+ # ENV["FB_APP_ID"] ||= "app id"
11
+ # ENV["FB_APP_SECRET"] ||= "secret"
12
+ # ENV['GOOGLE_APP_ID'] = "app id"
13
+ # ENV['GOOGLE_APP_SECRET'] = "secret"
14
+ # #
15
+ # # Then, require the actual OAuth2 controller class (Plezi::OAuth2Ctrl).
16
+ # #
17
+ # require 'plezi/oauth'
18
+ # #
19
+ # # Last, but not least, remember to add the authentication route in the correct placement using:
20
+ # create_auth_shared_route do |service_name, remote_user_id, remote_user_email, remote_response|
21
+ # # ...callback for authentication.
22
+ # # This callback should return the app user object or false
23
+ # # This callback hass access to the magic controller methods (request, cookies, etc')
24
+ # end
@@ -5,8 +5,7 @@ if defined? Redis
5
5
  # ## Plezi Redis Automation
6
6
  # ## ====
7
7
  # ##
8
- # ## sets up Plezi to use Radis broadcast.
9
- # ## this is less recommended then writing your own tailored solution
8
+ # ## Sets up Plezi to use Radis broadcast.
10
9
  # ##
11
10
  # ## If Plezi Redis Automation is enabled:
12
11
  # ## Plezi creates is own listening thread for each Controller class that broadcasts using Redis.
@@ -15,17 +14,17 @@ if defined? Redis
15
14
  # ## this overrides the default Controller#broadcast method which is very powerful but
16
15
  # ## is limited to one process.
17
16
  # ##
18
- # ENV['PL_REDIS_URL'] = ENV['REDISCLOUD_URL'] ||= ENV["REDISTOGO_URL"] ||= "redis://username:password@my.host:6389"
17
+ # ENV['PL_REDIS_URL'] ||= ENV['REDIS_URL'] || ENV['REDISCLOUD_URL'] || ENV['REDISTOGO_URL'] || "redis://username:password@my.host:6389"
19
18
 
20
19
 
21
- # ## Custom Redis Automation
20
+ # ## OR, write your own custom Redis Automation here
22
21
  # ## ====
23
22
  # ##
24
23
  # ## create a listening thread - rewrite the following code for your own Redis tailored solution.
25
24
  # ##
26
25
  # ## the following is only sample code for you to change:
27
26
  # RADIS_CHANNEL = 'appsecret'
28
- # RADIS_URI = URI.parse(ENV['REDISCLOUD_URL'] || "redis://username:password@my.host:6389")
27
+ # RADIS_URI = URI.parse(ENV['REDIS_URL'] || ENV['REDISCLOUD_URL'] || "redis://username:password@my.host:6389")
29
28
  # RADIS_CONNECTION = Redis.new(host: RADIS_URI.host, port: RADIS_URI.port, password: RADIS_URI.password)
30
29
  # RADIS_THREAD = Thread.new do
31
30
  # Redis.new(host: RADIS_URI.host, port: RADIS_URI.port, password: RADIS_URI.password).subscribe(RADIS_CHANNEL) do |on|
@@ -0,0 +1,235 @@
1
+ # I started writing tests... but I haven't finished quite yet.
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'open-uri'
5
+ require 'plezi'
6
+
7
+ class TestCtrl
8
+
9
+ # this will be called before every request.
10
+ def before
11
+ end
12
+
13
+ # this will be called after every request.
14
+ def after
15
+ end
16
+
17
+ # shouldn't be available (return 404).
18
+ def _hidden
19
+ "do you see me?"
20
+ end
21
+ def index
22
+ "test"
23
+ end
24
+
25
+ # should return a 500 internal server error message.
26
+ def fail
27
+ raise "Hell!"
28
+ end
29
+
30
+ # called when request is GET and params\[:id] == "new".
31
+ def new
32
+ "new"
33
+ end
34
+ # called when request is GET and params\[:id] exists (unless params\[:id] == "new").
35
+ def show
36
+ "show #{params[:id]}"
37
+ end
38
+ # called when request is POST / PUT and params\[:id] exists and isn't 'new'
39
+ def update
40
+ "update #{params[:id]}"
41
+ end
42
+ def delete
43
+ "delete #{params[:id]}"
44
+ end
45
+ # called when request is POST / PUT and params\[:id] is 'new'
46
+ def save
47
+ params[:data].to_s
48
+ end
49
+ def sleeper
50
+ sleep 1
51
+ "slept"
52
+ end
53
+ # should return a 404 error.
54
+ def get404
55
+ false
56
+ end
57
+ # path to test for chuncked encoding and response streaming.
58
+ def streamer
59
+ response.start_http_streaming
60
+ PL.callback(self, :_stream_out) { PL.callback(response, :finish) }
61
+ true
62
+ end
63
+ def _stream_out
64
+ response.send "streamed"
65
+ true
66
+ end
67
+ def file_test
68
+ if params[:file]
69
+ send_data params[:file][:data], type: params[:file][:type], inline: true, filename: params[:file][:filename]
70
+ return true
71
+ end
72
+ false
73
+ end
74
+
75
+
76
+ ############
77
+ ## WebSockets
78
+
79
+ # called once the websocket was connected
80
+ def on_connect
81
+ response << "connected"
82
+ end
83
+
84
+ # called when new Websocket data is recieved
85
+ #
86
+ # data is a string that contains binary or UTF8 (message dependent) data.
87
+ def on_message data
88
+ broadcast :_push, data
89
+ _push data
90
+ end
91
+
92
+ # called when a disconnect packet has been recieved or the connection has been cut
93
+ # (ISN'T called after a disconnect message has been sent).
94
+ def on_disconnect
95
+ end
96
+
97
+ # a demo event method that recieves a broadcast from instance siblings.
98
+ def _push data
99
+ response << data.to_s
100
+ end
101
+ end
102
+
103
+ module PleziTestTasks
104
+ module_function
105
+
106
+ RESULTS = {true => "passed", false => 'FAILED!'}
107
+
108
+ def run_tests
109
+ (public_methods(false)).each {|m| method(m).call if m.to_s.match /^test_/}
110
+ true
111
+ end
112
+ def test_sleep
113
+ Plezi.run_async do
114
+ begin
115
+ puts "Sleeper test: #{RESULTS[URI.parse("http://localhost:3000/sleeper").read == 'slept']}"
116
+ puts "ASync tasks test: #{RESULTS[true]}"
117
+ rescue => e
118
+ puts "Sleeper test FAILED TO RUN!!!"
119
+ puts e
120
+ end
121
+ end
122
+ end
123
+
124
+ def test_index
125
+ begin
126
+ puts "index test: #{RESULTS[URI.parse("http://localhost:3000/").read == 'test']}"
127
+ rescue => e
128
+ puts "Index test FAILED TO RUN!!!"
129
+ puts e
130
+ end
131
+ end
132
+ def test_ssl
133
+ puts "Connection to non-ssl and unique route test: #{RESULTS[URI.parse("http://localhost:3000/ssl").read == 'false']}"
134
+ uri = URI.parse("https://localhost:3030/ssl")
135
+ Net::HTTP.start(uri.host, uri.port, use_ssl: (uri.scheme == "https"), verify_mode: OpenSSL::SSL::VERIFY_NONE) do |http|
136
+ puts "Connection to ssl and unique ssl route test: #{RESULTS[ http.request(Net::HTTP::Get.new(uri)).body == 'true' ]}"
137
+ end
138
+ rescue => e
139
+ puts "SSL Tests FAILED to complete!!!"
140
+ puts e
141
+ end
142
+ def test_new
143
+ puts "New RESTful path test: #{RESULTS[URI.parse("http://localhost:3000/new").read == 'new']}"
144
+
145
+ rescue => e
146
+ puts "New RESTful path test FAILED TO RUN!!!"
147
+ puts e
148
+ end
149
+ def test_show
150
+ puts "Show RESTful path test: #{RESULTS[URI.parse("http://localhost:3000/3").read == 'show 3']}"
151
+
152
+ rescue => e
153
+ puts "Show RESTful path test FAILED TO RUN!!!"
154
+ puts e
155
+ end
156
+ def test_update
157
+ puts "Update RESTful path test: #{RESULTS[Net::HTTP.post_form( URI.parse("http://localhost:3000/"), id: 3).body == 'update 3']}"
158
+
159
+ rescue => e
160
+ puts "Update RESTful path test FAILED TO RUN!!!"
161
+ puts e
162
+ end
163
+ def test_delete
164
+ puts "Delete RESTful path test: #{RESULTS[Net::HTTP.post_form( URI.parse("http://localhost:3000/"), id: 3, _method: :delete).body == 'delete 3']}"
165
+
166
+ rescue => e
167
+ puts "Delete RESTful path test FAILED TO RUN!!!"
168
+ puts e
169
+ end
170
+ def test_save
171
+ puts "Save RESTful path test: #{RESULTS[Net::HTTP.post_form( URI.parse("http://localhost:3000/new"), data: "passed").body == 'passed']}"
172
+
173
+ rescue => e
174
+ puts "Save RESTful path test FAILED TO RUN!!!"
175
+ puts e
176
+ end
177
+ def test_streamed
178
+ begin
179
+ puts "Streaming test: #{RESULTS[URI.parse("http://localhost:3000/streamer").read == 'streamed']}"
180
+ rescue => e
181
+ puts "Streaming test FAILED TO RUN!!!"
182
+ puts e
183
+ end
184
+ end
185
+ def test_404
186
+ puts "404 not found and router continuity tests: #{RESULTS[ Net::HTTP.get_response(URI.parse "http://localhost:3000/get404" ).code == '404' ]}"
187
+
188
+ rescue => e
189
+ puts "404 not found test FAILED TO RUN!!!"
190
+ puts e
191
+ end
192
+ def test_500
193
+ workers = Plezi::EventMachine.count_living_workers
194
+ puts "500 internal error test: #{RESULTS[ Net::HTTP.get_response(URI.parse "http://localhost:3000/fail" ).code == '500' ]}"
195
+ # cause 10 more exceptions to be raised... testing thread survival.
196
+ 10.times { Net::HTTP.get_response(URI.parse "http://localhost:3000/fail" ).code }
197
+ workers_after_test = Plezi::EventMachine.count_living_workers
198
+ puts "Worker survival test: #{RESULTS[workers_after_test == workers]} (#{workers_after_test} out of #{workers})"
199
+
200
+ rescue => e
201
+ puts "404 not found test FAILED TO RUN!!!"
202
+ puts e
203
+ end
204
+ end
205
+
206
+ NO_PLEZI_AUTO_START = true
207
+
208
+ PL.create_logger '/dev/null'
209
+
210
+ listen port: 3000
211
+
212
+ route("/ssl") {|req, res| res << "false" }
213
+ listen port: 3030, ssl: true
214
+ route("/ssl") {|req, res| res << "true" }
215
+
216
+ shared_route '/', TestCtrl
217
+
218
+
219
+ Plezi::EventMachine.start Plezi.max_threads
220
+
221
+ shoutdown_test = false
222
+ Plezi.on_shutdown { shoutdown_test = true }
223
+
224
+ PleziTestTasks.run_tests
225
+
226
+ Plezi::EventMachine.clear_timers
227
+
228
+ Plezi::DSL.stop_services
229
+
230
+ Plezi::EventMachine.shutdown
231
+
232
+
233
+ puts "Shutdown test: #{ PleziTestTasks::RESULTS[shoutdown_test] }"
234
+
235
+
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.8.3
4
+ version: 0.8.4
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-05-30 00:00:00.000000000 Z
11
+ date: 2015-06-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -74,6 +74,8 @@ files:
74
74
  - lib/plezi/handlers/magic_helpers.rb
75
75
  - lib/plezi/handlers/route.rb
76
76
  - lib/plezi/handlers/stubs.rb
77
+ - lib/plezi/oauth.rb
78
+ - lib/plezi/oauth/auth_controller.rb
77
79
  - lib/plezi/server/http.rb
78
80
  - lib/plezi/server/http_protocol.rb
79
81
  - lib/plezi/server/http_request.rb
@@ -102,12 +104,14 @@ files:
102
104
  - resources/environment.rb
103
105
  - resources/haml_config.rb
104
106
  - resources/i18n_config.rb
107
+ - resources/oauth_config.rb
105
108
  - resources/plezi_gray.png
106
109
  - resources/plezi_websockets.html
107
110
  - resources/rakefile
108
111
  - resources/redis_config.rb
109
112
  - resources/routes.rb
110
113
  - resources/welcome_page.html
114
+ - test/plezi_tests.rb
111
115
  - websocket chatroom.md
112
116
  homepage: http://boazsegev.github.io/plezi/
113
117
  licenses:
@@ -135,4 +139,5 @@ signing_key:
135
139
  specification_version: 4
136
140
  summary: Plezi is the native Ruby Framework for real time web-apps, with Websockets,
137
141
  RESTful routing and HTTP streaming support.
138
- test_files: []
142
+ test_files:
143
+ - test/plezi_tests.rb