plezi 0.8.4 → 0.8.5
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
|