plezi 0.8.4 → 0.8.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +10 -0
- data/README.md +48 -1
- data/Rakefile +7 -1
- data/bin/plezi +7 -0
- data/lib/plezi.rb +2 -0
- data/lib/plezi/eventmachine/ssl_connection.rb +9 -3
- data/lib/plezi/handlers/controller_magic.rb +43 -1
- data/lib/plezi/handlers/magic_helpers.rb +9 -2
- data/lib/plezi/handlers/route.rb +108 -51
- data/lib/plezi/oauth.rb +3 -2
- data/lib/plezi/oauth/auth_controller.rb +48 -27
- data/lib/plezi/server/http_protocol.rb +11 -0
- data/lib/plezi/server/websocket_client.rb +172 -0
- data/lib/plezi/version.rb +1 -1
- data/resources/Gemfile +1 -0
- data/resources/database.yml +33 -0
- data/resources/db_ac_config.rb +34 -33
- data/resources/environment.rb +2 -2
- data/resources/oauth_config.rb +2 -2
- data/resources/rakefile +3 -3
- data/test/plezi_tests.rb +104 -27
- metadata +4 -3
- data/TODO.md +0 -15
data/lib/plezi/oauth.rb
CHANGED
@@ -1,61 +1,80 @@
|
|
1
|
-
|
1
|
+
|
2
2
|
|
3
3
|
|
4
4
|
|
5
5
|
module Plezi
|
6
6
|
|
7
7
|
#########################################
|
8
|
-
# This is a social Authentication Controller
|
8
|
+
# This is a social Authentication Controller for OAuth2 services.
|
9
|
+
# This controller allows you to easily deploy Facebook Login, Google Login and any other OAuth2 complient login service.
|
10
|
+
#
|
11
|
+
# To include this controller in your application, you need to require it using:
|
12
|
+
#
|
13
|
+
# require 'plezi/oauth'
|
9
14
|
#
|
10
|
-
#
|
15
|
+
# It is possible to manualy register any OAuth 2.0 authentication service using the `register_service` method:
|
16
|
+
#
|
17
|
+
# register_service(:foo,
|
18
|
+
# app_id: 'registered app id / client id',
|
19
|
+
# app_secret: 'registered app secret / client secret',
|
20
|
+
# auth_url: "https://foo.bar.com/o/oauth2/auth",
|
21
|
+
# token_url: "https://foo.bar.com/oauth2/v3/token",
|
22
|
+
# profile_url: "https://foo.bar/oauth2/v1/userinfo",
|
23
|
+
# scope: "profile email")
|
24
|
+
#
|
25
|
+
# The `.register_service` method will be automatically called for the following login services:
|
11
26
|
#
|
12
27
|
# - 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
28
|
# - Google authentication using the OAuth 2.0 API - [see Google documentation](https://developers.google.com/identity/protocols/OAuth2WebServer).
|
14
29
|
#
|
15
|
-
#
|
16
|
-
#
|
17
|
-
# To use this Controller in your application, make sure the following variables are set:
|
30
|
+
# To enjoy autorgistration for Facebook or Google, make sure the following environment variables are set:
|
18
31
|
# ENV['FB_APP_ID'] = {facebook_app_id}
|
19
32
|
# ENV['FB_APP_SECRET'] = {facebook_app_secret}
|
20
33
|
# ENV['GOOGLE_APP_ID'] = {google_app_id}
|
21
34
|
# ENV['GOOGLE_APP_SECRET'] = {google_app_secret}
|
22
35
|
#
|
23
|
-
#
|
36
|
+
# To add the OAuth routes to the routes, use the following short-cut method (add it to the `routes.rb`) file:
|
24
37
|
#
|
25
38
|
# create_auth_shared_route do |service, remote_user_id, email, full_remote_response|
|
26
39
|
# # perform any specific app authentication logic such as saving the info.
|
27
40
|
# # return the current user or false if the callback is called with an authentication failure.
|
28
41
|
# end
|
29
42
|
#
|
30
|
-
#
|
43
|
+
# \* Notice that, as always, route placement order effects behavior, so that routes are checked according to order of creation.
|
44
|
+
#
|
45
|
+
# The `create_auth_shared_route` method is a shortcut taht calls the `#shared_route` method with the relevant arguments and sets the OAuth2Ctrl callback.
|
31
46
|
#
|
32
47
|
# Use the following links for social authentication:
|
33
48
|
#
|
34
|
-
# - Facebook: "/auth/facebook
|
35
|
-
# - Google: "/auth/google
|
36
|
-
# - foo_service: "/auth/foo_service
|
49
|
+
# - Facebook: "/auth/facebook"
|
50
|
+
# - Google: "/auth/google"
|
51
|
+
# - foo_service: "/auth/foo_service"
|
37
52
|
#
|
53
|
+
# You can control the page to which the user will return once authentication is complete
|
54
|
+
# (even when authentication fails) by setting the "redirect_after" parameter into the GET request in the url. for example:
|
55
|
+
#
|
56
|
+
# - Google: "/auth/google?redirect_after=/foo/bar"
|
38
57
|
class OAuth2Ctrl
|
39
58
|
|
40
|
-
# Sets (or gets) the callback to be called after authentication is attempeted.
|
59
|
+
# Sets (or gets) the global callback to be called after authentication is attempeted.
|
41
60
|
#
|
42
61
|
# Accepts a block that will be called with the following parameters:
|
43
62
|
# service_name:: the name of the service. i.e. :facebook, :google, etc'.
|
44
63
|
# service_token:: the authentication token returned by the service. This token should be stored for future access
|
45
64
|
# remote_user_id:: service's user id.
|
46
|
-
# remote_user_email::
|
65
|
+
# remote_user_email:: user's primamry email, as (and if) registed with the service.
|
47
66
|
# remote_response:: a Hash with the complete user data response (including email and id).
|
48
67
|
#
|
49
|
-
# If the authentication fails for
|
68
|
+
# If the authentication fails for the service, the block will be called with the following values `auth_callback.call(nil, ni, {server: :responce, might_be: :empty})`
|
50
69
|
#
|
51
70
|
# The block will be run in the context of the controller and all the controller's methods will be available to it.
|
52
71
|
#
|
53
72
|
# i.e.:
|
54
|
-
# OAuth2Ctrl.auth_callback |service, service_token, id, email,
|
73
|
+
# OAuth2Ctrl.auth_callback |service, service_token, id, email, full_res| PL.info "OAuth got: #{full_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 }
|
55
74
|
#
|
56
75
|
# defaults to the example above, which isn't a very sercure behavior, but allows for easy testing.
|
57
76
|
def self.auth_callback &block
|
58
|
-
block_given? ? (
|
77
|
+
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
78
|
end
|
60
79
|
|
61
80
|
|
@@ -69,20 +88,20 @@ module Plezi
|
|
69
88
|
# options:: a Hash of options, some of which are required.
|
70
89
|
#
|
71
90
|
# The options are:
|
72
|
-
# app_id::
|
73
|
-
# app_secret::
|
74
|
-
# auth_url::
|
75
|
-
# token_url::
|
76
|
-
# profile_url::
|
91
|
+
# app_id:: Required. 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)
|
92
|
+
# app_secret:: Required. The aplication's unique secret registered with the service.
|
93
|
+
# auth_url:: Required. The authentication URL. This is the url to which the user is redirected. i.e.: "https://www.facebook.com/dialog/oauth"
|
94
|
+
# token_url:: Required. 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"
|
95
|
+
# profile_url:: Required. 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
96
|
# scope:: a String representing the scope requested. i.e. 'email profile'.
|
78
97
|
#
|
79
|
-
# There will be an attempt to
|
98
|
+
# There will be an attempt to automatically register Facebook and Google login services under these conditions:
|
80
99
|
#
|
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.
|
100
|
+
# * For Facebook: Both ENV ['FB_APP_ID'] && ENV ['FB_APP_SECRET'] have been defined.
|
101
|
+
# * For Google: Both ENV ['GOOGLE_APP_ID'] && ENV ['GOOGLE_APP_SECRET'] have been defined.
|
83
102
|
#
|
84
103
|
#
|
85
|
-
#
|
104
|
+
# The auto registration uses the following urls (updated to June 5, 2015):
|
86
105
|
#
|
87
106
|
# * facebook auth_url: "https://www.facebook.com/dialog/oauth"
|
88
107
|
# * facebook token_url: "https://graph.facebook.com/v2.3/oauth/access_token"
|
@@ -91,6 +110,8 @@ module Plezi
|
|
91
110
|
# * google token_url: "https://www.googleapis.com/oauth2/v3/token"
|
92
111
|
# * google profile_url: "https://www.googleapis.com/plus/v1/people/me"
|
93
112
|
#
|
113
|
+
# to change the default url's for Facebook or Google, simpley re-register the service using this method.
|
114
|
+
#
|
94
115
|
def self.register_service service_name, options
|
95
116
|
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
117
|
# for google, scope is space delimited. for facebook it's comma delimited
|
@@ -201,8 +222,8 @@ end
|
|
201
222
|
#
|
202
223
|
# The method can be called only once and will self-destruct.
|
203
224
|
def create_auth_shared_route options = {}, &block
|
204
|
-
shared_route "auth/(:id)
|
225
|
+
shared_route "auth/(:id)" , Plezi::OAuth2Ctrl
|
205
226
|
undef create_auth_shared_route
|
206
|
-
Plezi::OAuth2Ctrl.auth_callback
|
227
|
+
Plezi::OAuth2Ctrl.auth_callback &block if block
|
207
228
|
Plezi::OAuth2Ctrl
|
208
229
|
end
|
@@ -296,6 +296,17 @@ module Plezi
|
|
296
296
|
end
|
297
297
|
end
|
298
298
|
|
299
|
+
|
300
|
+
# # HTTPProtocol - to do list
|
301
|
+
#
|
302
|
+
# XML::
|
303
|
+
# support for XML HTTP body types?
|
304
|
+
#
|
305
|
+
# Charset::
|
306
|
+
# parse chareset for incoming content-type in the multipart request body? (or leave if binary?)
|
307
|
+
|
308
|
+
|
309
|
+
|
299
310
|
## Heroku/extra headers info
|
300
311
|
|
301
312
|
# All headers are considered to be case-insensitive, as per HTTP Specification.
|
@@ -0,0 +1,172 @@
|
|
1
|
+
module Plezi
|
2
|
+
|
3
|
+
# Websocket client objects are members of this class.
|
4
|
+
#
|
5
|
+
# This is a VERY simple Websocket client. It doesn't support cookies, HTTP authentication or... well... anything, really.
|
6
|
+
# It's just a simple client used for the Plezi framework's testing. It's usful for simple WebSocket connections, but no more.
|
7
|
+
class WebsocketClient
|
8
|
+
attr_accessor :response, :request
|
9
|
+
|
10
|
+
class RequestEmulator < Hash
|
11
|
+
def service
|
12
|
+
self[:connection]
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def initialize request
|
17
|
+
@response = WSResponse.new request
|
18
|
+
@options = request[:options]
|
19
|
+
@on_message = @options[:on_message]
|
20
|
+
raise "Websocket client must have an #on_message Proc." unless @on_message && @on_message.is_a?(Proc)
|
21
|
+
@on_connect = @options[:on_connect]
|
22
|
+
@on_disconnect = @options[:on_disconnect]
|
23
|
+
end
|
24
|
+
|
25
|
+
def on_message(data = false, &block)
|
26
|
+
unless data
|
27
|
+
@on_message = block if block
|
28
|
+
return @on_message
|
29
|
+
end
|
30
|
+
instance_exec( data, &@on_message)
|
31
|
+
end
|
32
|
+
|
33
|
+
def on_connect(&block)
|
34
|
+
if block
|
35
|
+
@on_connect = block
|
36
|
+
return @on_connect
|
37
|
+
end
|
38
|
+
instance_exec(&@on_connect) if @on_connect
|
39
|
+
end
|
40
|
+
|
41
|
+
def on_disconnect(&block)
|
42
|
+
if block
|
43
|
+
@on_disconnect = block
|
44
|
+
return @on_disconnect
|
45
|
+
end
|
46
|
+
instance_exec(&@on_disconnect) if @on_disconnect
|
47
|
+
end
|
48
|
+
|
49
|
+
#disconnects the Websocket.
|
50
|
+
def disconnect
|
51
|
+
@response.close if @response
|
52
|
+
end
|
53
|
+
|
54
|
+
# sends data through the socket. a shortcut for ws_client.response <<
|
55
|
+
def << data
|
56
|
+
@response << data
|
57
|
+
end
|
58
|
+
|
59
|
+
# Create a simple Websocket Client(!)
|
60
|
+
#
|
61
|
+
# This method accepts two parameters:
|
62
|
+
# url:: a String representing the URL of the websocket. i.e.: 'ws://foo.bar.com:80/ws/path'
|
63
|
+
# options:: a Hash with options to be used. The options will be used to define
|
64
|
+
# &block:: an optional block that accepts one parameter (data) and will be used as the `#on_message(data)`
|
65
|
+
#
|
66
|
+
# The method will either return a WebsocketClient instance object or it will raise an exception.
|
67
|
+
#
|
68
|
+
# An on_message Proc must be defined, or the method will fail.
|
69
|
+
#
|
70
|
+
# The on_message Proc can be defined using the optional block:
|
71
|
+
#
|
72
|
+
# WebsocketClient.connect_to("ws://localhost:3000/") {|data| response << data} #echo example
|
73
|
+
#
|
74
|
+
# OR, the on_message Proc can be defined using the options Hash:
|
75
|
+
#
|
76
|
+
# WebsocketClient.connect_to("ws://localhost:3000/", on_connect: -> {}, on_message: -> {|data| response << data})
|
77
|
+
#
|
78
|
+
# The #on_message(data), #on_connect and #on_disconnect methods will be executed within the context of the WebsocketClient
|
79
|
+
# object, and will have natice acess to the Websocket response object.
|
80
|
+
#
|
81
|
+
# After the WebsocketClient had been created, it's possible to update the #on_message and #on_disconnect methods:
|
82
|
+
#
|
83
|
+
# # updates #on_message
|
84
|
+
# wsclient.on_message do |data|
|
85
|
+
# response << "I'll disconnect on the next message!"
|
86
|
+
# # updates #on_message again.
|
87
|
+
# on_message {|data| disconnect }
|
88
|
+
# end
|
89
|
+
#
|
90
|
+
#
|
91
|
+
# !!please be aware that the Websockt Client will not attempt to verify SSL certificates,
|
92
|
+
# so that even SSL connections are subject to a possible man in the middle attack.
|
93
|
+
def self.connect_to url, options={}, &block
|
94
|
+
options[:on_message] ||= block
|
95
|
+
options[:handler] = WebsocketClient
|
96
|
+
options[:protocol] = EventMachine::Protocol
|
97
|
+
url = URI.parse(url) unless url.is_a?(URI)
|
98
|
+
connection_type = EventMachine::Connection
|
99
|
+
|
100
|
+
socket = false #implement the connection, ssl vs. no ssl
|
101
|
+
if url.scheme == "https" || url.scheme == "wss"
|
102
|
+
connection_type = EventMachine::SSLConnection
|
103
|
+
options[:ssl_client] = true
|
104
|
+
url.port ||= 443
|
105
|
+
end
|
106
|
+
url.port ||= 80
|
107
|
+
socket = TCPSocket.new(url.host, url.port)
|
108
|
+
connection = connection_type.new socket, options
|
109
|
+
psedo_request = RequestEmulator.new
|
110
|
+
psedo_request[:connection] = connection
|
111
|
+
psedo_request[:client_ip] = 'WS Client'
|
112
|
+
psedo_request[:url] = url
|
113
|
+
psedo_request[:options] = options
|
114
|
+
WSProtocol.client_handshake psedo_request
|
115
|
+
connection.handler
|
116
|
+
rescue => e
|
117
|
+
socket.close if socket
|
118
|
+
raise e
|
119
|
+
end
|
120
|
+
|
121
|
+
end
|
122
|
+
|
123
|
+
class WSProtocol < EventMachine::Protocol
|
124
|
+
def self.client_handshake psedo_request, timeout = 5
|
125
|
+
connection = psedo_request[:connection]
|
126
|
+
url = psedo_request[:url]
|
127
|
+
# send protocol upgrade request
|
128
|
+
websocket_key = [(Array.new(16) {rand 255} .pack 'c*' )].pack('m0*')
|
129
|
+
connection.send "GET #{url.path}#{url.query.to_s.empty? ? '' : ('?' + url.query)} HTTP/1.1\r\nHost: #{url.host}#{url.port ? (':'+url.port.to_s) : ''}\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Key: #{websocket_key}\r\nSec-WebSocket-Version: 13\r\n\r\n"
|
130
|
+
# wait for answer - make sure we don't over-read
|
131
|
+
# (a websocket message might be sent immidiately after connection is established)
|
132
|
+
reply = ''
|
133
|
+
reply.force_encoding('binary')
|
134
|
+
start_time = Time.now
|
135
|
+
stop_reply = "\r\n\r\n"
|
136
|
+
until reply[-4..-1] == stop_reply
|
137
|
+
(reply << connection.read(1)) rescue (sleep 0.1)
|
138
|
+
raise Timeout::Error, "Websocket client handshake timed out (HTTP reply not recieved)\n\n Got Only: #{reply.dump}" if Time.now >= (start_time + 5)
|
139
|
+
end
|
140
|
+
# review reply
|
141
|
+
raise 'Connection Refused.' unless reply.lines[0].match(/^HTTP\/[\d\.]+ 101/i)
|
142
|
+
raise 'Websocket Key Authentication failed.' unless reply.match(/^Sec-WebSocket-Accept:[\s]*([^\s]*)/i) && reply.match(/^Sec-WebSocket-Accept:[\s]*([^\s]*)/i)[1] == Digest::SHA1.base64digest(websocket_key + '258EAFA5-E914-47DA-95CA-C5AB0DC85B11')
|
143
|
+
# set-up handler response object.
|
144
|
+
connection.handler = WebsocketClient.new psedo_request
|
145
|
+
|
146
|
+
# raise "not yet implemented"
|
147
|
+
|
148
|
+
# set the connetion's protocol to a new WSProtocol instance
|
149
|
+
connection.protocol = self.new psedo_request[:connection], psedo_request[:options]
|
150
|
+
# add the socket to the EventMachine IO reactor
|
151
|
+
EventMachine.add_io connection.socket, connection
|
152
|
+
true
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
|
158
|
+
######
|
159
|
+
## example requests
|
160
|
+
|
161
|
+
# GET /nickname HTTP/1.1
|
162
|
+
# Upgrade: websocket
|
163
|
+
# Connection: Upgrade
|
164
|
+
# Host: localhost:3000
|
165
|
+
# Origin: https://www.websocket.org
|
166
|
+
# Cookie: test=my%20cookies; user_token=2INa32_vDgx8Aa1qe43oILELpSdIe9xwmT8GTWjkS-w
|
167
|
+
# Pragma: no-cache
|
168
|
+
# Cache-Control: no-cache
|
169
|
+
# Sec-WebSocket-Key: 1W9B64oYSpyRL/yuc4k+Ww==
|
170
|
+
# Sec-WebSocket-Version: 13
|
171
|
+
# Sec-WebSocket-Extensions: x-webkit-deflate-frame
|
172
|
+
# User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10) AppleWebKit/600.1.25 (KHTML, like Gecko) Version/8.0 Safari/600.1.25
|
data/lib/plezi/version.rb
CHANGED
data/resources/Gemfile
CHANGED
@@ -86,3 +86,4 @@ gem 'plezi'
|
|
86
86
|
## but first, please remember that AcriveRecord needs extra attention when multi-threading
|
87
87
|
# gem 'activerecord', :require => 'active_record'
|
88
88
|
# gem 'bcrypt', '~> 3.1.7'
|
89
|
+
# gem 'standalone_migrations' # will provide better rake tasks support for ActiveRecord
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# The following are common database setting as recomended by Heroku:
|
2
|
+
default: &default
|
3
|
+
adapter: postgresql
|
4
|
+
host: localhost
|
5
|
+
username: user
|
6
|
+
|
7
|
+
development:
|
8
|
+
<<: *default
|
9
|
+
database: app-dev
|
10
|
+
|
11
|
+
production:
|
12
|
+
<<: *default
|
13
|
+
database: app-dev
|
14
|
+
|
15
|
+
# # Here is an Sqlite3 example DB.
|
16
|
+
# # You can use it to update the default if you want to use Sqlite3
|
17
|
+
# sqlite3_db:
|
18
|
+
# adapter: sqlite3
|
19
|
+
# pool: 5
|
20
|
+
# timeout: 5000
|
21
|
+
# database: db/db.sqlite3
|
22
|
+
|
23
|
+
|
24
|
+
# # Here is a MySQL example DB.
|
25
|
+
# # You can use it to update the default if you want to use MySQL
|
26
|
+
# mysql_db:
|
27
|
+
# adapter: mysql2
|
28
|
+
# encoding: utf8
|
29
|
+
# username: root
|
30
|
+
# password: password
|
31
|
+
# host: localhost
|
32
|
+
# port: 3306
|
33
|
+
# database: database_name
|
data/resources/db_ac_config.rb
CHANGED
@@ -7,19 +7,13 @@
|
|
7
7
|
#
|
8
8
|
############
|
9
9
|
## ActiveRecord without Rails
|
10
|
-
# more info
|
10
|
+
# more info at:
|
11
11
|
# https://www.youtube.com/watch?v=o0SzhgYntK8
|
12
12
|
# demo code here:
|
13
13
|
# https://github.com/stungeye/ActiveRecord-without-Rails
|
14
14
|
if defined? ActiveRecord
|
15
|
-
|
16
|
-
|
17
|
-
ActiveRecord::Base.establish_connection :adapter => 'sqlite3', :database => Root.join('db','db.sqlite3').to_s, encoding: 'unicode'
|
18
|
-
elsif defined? PG
|
19
|
-
ActiveRecord::Base.establish_connection :adapter => 'postgresql', encoding: 'unicode'
|
20
|
-
else
|
21
|
-
Plezi.logger.warning "ActiveRecord database adapter not auto-set. Update the db_ac_config.rb file"
|
22
|
-
end
|
15
|
+
puts "Loading ActiveRecord database setting: #{ENV['ENV']}"
|
16
|
+
ActiveRecord::Base.establish_connection( YAML::load( File.open( Root.join('db', 'config.yml').to_s ) )[ ENV["ENV"].to_s ] )
|
23
17
|
|
24
18
|
# if debugging purposes, uncomment this line to see the ActiveRecord's generated SQL:
|
25
19
|
# ActiveRecord::Base.logger = Plezi.logger
|
@@ -27,32 +21,39 @@ if defined? ActiveRecord
|
|
27
21
|
# Uncomment this line to make the logger output look nicer in Windows.
|
28
22
|
# ActiveSupport::LogSubscriber.colorize_logging = false
|
29
23
|
|
24
|
+
# Load ActiveRecord Tasks, if implemented
|
30
25
|
if defined? Rake
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
26
|
+
|
27
|
+
begin
|
28
|
+
require 'standalone_migrations'
|
29
|
+
StandaloneMigrations::Tasks.load_tasks
|
30
|
+
rescue Exception => e
|
31
|
+
ActiveRecord::Tasks::DatabaseTasks.env = ENV['ENV'] || 'development'
|
32
|
+
ActiveRecord::Tasks::DatabaseTasks.database_configuration = YAML.load(File.read(Root.join('db', 'config.yml').to_s))
|
33
|
+
ActiveRecord::Tasks::DatabaseTasks.db_dir = Root.join('db').to_s
|
34
|
+
ActiveRecord::Tasks::DatabaseTasks.fixtures_path = Root.join( 'db', 'fixtures').to_s
|
35
|
+
ActiveRecord::Tasks::DatabaseTasks.migrations_paths = [Root.join('db', 'migrate').to_s]
|
36
|
+
ActiveRecord::Tasks::DatabaseTasks.seed_loader = Class.new do
|
37
|
+
def self.load_seed
|
38
|
+
filename = Root.join('db', 'seeds.rb').to_s
|
39
|
+
unless File.file?(filename)
|
40
|
+
IO.write filename, ''
|
41
|
+
end
|
42
|
+
load filename
|
43
|
+
end
|
44
|
+
end
|
45
|
+
ActiveRecord::Tasks::DatabaseTasks.root = Root.to_s
|
46
|
+
|
47
|
+
task :environment do
|
48
|
+
ActiveRecord::Base.configurations = ActiveRecord::Tasks::DatabaseTasks.database_configuration
|
49
|
+
ActiveRecord::Base.establish_connection ActiveRecord::Tasks::DatabaseTasks.env.to_sym
|
50
|
+
end
|
51
|
+
|
52
|
+
load 'active_record/railties/databases.rake'
|
53
|
+
|
54
|
+
end
|
55
55
|
end
|
56
56
|
|
57
|
+
|
57
58
|
end
|
58
59
|
|