tenderlove-facebooker 1.0.16.20090319151701
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.
- data/CHANGELOG.rdoc +17 -0
- data/COPYING.rdoc +19 -0
- data/Manifest.txt +123 -0
- data/README.rdoc +103 -0
- data/Rakefile +86 -0
- data/TODO.rdoc +4 -0
- data/examples/desktop_login.rb +14 -0
- data/facebooker.gemspec +38 -0
- data/generators/facebook/facebook_generator.rb +14 -0
- data/generators/facebook/templates/config/facebooker.yml +46 -0
- data/generators/facebook/templates/public/javascripts/facebooker.js +99 -0
- data/generators/facebook_controller/USAGE +33 -0
- data/generators/facebook_controller/facebook_controller_generator.rb +40 -0
- data/generators/facebook_controller/templates/controller.rb +7 -0
- data/generators/facebook_controller/templates/functional_test.rb +11 -0
- data/generators/facebook_controller/templates/helper.rb +2 -0
- data/generators/facebook_controller/templates/view.fbml.erb +2 -0
- data/generators/facebook_controller/templates/view.html.erb +2 -0
- data/generators/facebook_publisher/facebook_publisher_generator.rb +14 -0
- data/generators/facebook_publisher/templates/create_facebook_templates.rb +15 -0
- data/generators/facebook_publisher/templates/publisher.rb +3 -0
- data/generators/facebook_scaffold/USAGE +27 -0
- data/generators/facebook_scaffold/facebook_scaffold_generator.rb +118 -0
- data/generators/facebook_scaffold/templates/controller.rb +93 -0
- data/generators/facebook_scaffold/templates/facebook_style.css +2579 -0
- data/generators/facebook_scaffold/templates/functional_test.rb +89 -0
- data/generators/facebook_scaffold/templates/helper.rb +2 -0
- data/generators/facebook_scaffold/templates/layout.fbml.erb +6 -0
- data/generators/facebook_scaffold/templates/layout.html.erb +17 -0
- data/generators/facebook_scaffold/templates/style.css +74 -0
- data/generators/facebook_scaffold/templates/view_edit.fbml.erb +13 -0
- data/generators/facebook_scaffold/templates/view_edit.html.erb +18 -0
- data/generators/facebook_scaffold/templates/view_index.fbml.erb +24 -0
- data/generators/facebook_scaffold/templates/view_index.html.erb +24 -0
- data/generators/facebook_scaffold/templates/view_new.fbml.erb +12 -0
- data/generators/facebook_scaffold/templates/view_new.html.erb +17 -0
- data/generators/facebook_scaffold/templates/view_show.fbml.erb +10 -0
- data/generators/facebook_scaffold/templates/view_show.html.erb +10 -0
- data/generators/publisher/publisher_generator.rb +14 -0
- data/generators/xd_receiver/templates/xd_receiver.html +10 -0
- data/generators/xd_receiver/xd_receiver_generator.rb +9 -0
- data/init.rb +72 -0
- data/install.rb +12 -0
- data/lib/facebooker/adapters/adapter_base.rb +87 -0
- data/lib/facebooker/adapters/bebo_adapter.rb +75 -0
- data/lib/facebooker/adapters/facebook_adapter.rb +52 -0
- data/lib/facebooker/admin.rb +42 -0
- data/lib/facebooker/batch_request.rb +44 -0
- data/lib/facebooker/data.rb +57 -0
- data/lib/facebooker/feed.rb +78 -0
- data/lib/facebooker/logging.rb +51 -0
- data/lib/facebooker/mobile.rb +20 -0
- data/lib/facebooker/mock/service.rb +50 -0
- data/lib/facebooker/mock/session.rb +18 -0
- data/lib/facebooker/model.rb +135 -0
- data/lib/facebooker/models/affiliation.rb +10 -0
- data/lib/facebooker/models/album.rb +11 -0
- data/lib/facebooker/models/applicationproperties.rb +39 -0
- data/lib/facebooker/models/applicationrestrictions.rb +10 -0
- data/lib/facebooker/models/cookie.rb +10 -0
- data/lib/facebooker/models/education_info.rb +11 -0
- data/lib/facebooker/models/event.rb +28 -0
- data/lib/facebooker/models/friend_list.rb +16 -0
- data/lib/facebooker/models/group.rb +36 -0
- data/lib/facebooker/models/info_item.rb +10 -0
- data/lib/facebooker/models/info_section.rb +10 -0
- data/lib/facebooker/models/location.rb +8 -0
- data/lib/facebooker/models/notifications.rb +17 -0
- data/lib/facebooker/models/page.rb +27 -0
- data/lib/facebooker/models/photo.rb +12 -0
- data/lib/facebooker/models/tag.rb +12 -0
- data/lib/facebooker/models/user.rb +426 -0
- data/lib/facebooker/models/video.rb +9 -0
- data/lib/facebooker/models/work_info.rb +9 -0
- data/lib/facebooker/parser.rb +589 -0
- data/lib/facebooker/rails/controller.rb +304 -0
- data/lib/facebooker/rails/cucumber/world.rb +46 -0
- data/lib/facebooker/rails/cucumber.rb +28 -0
- data/lib/facebooker/rails/facebook_form_builder.rb +112 -0
- data/lib/facebooker/rails/facebook_pretty_errors.rb +22 -0
- data/lib/facebooker/rails/facebook_request_fix.rb +24 -0
- data/lib/facebooker/rails/facebook_session_handling.rb +69 -0
- data/lib/facebooker/rails/facebook_url_helper.rb +192 -0
- data/lib/facebooker/rails/facebook_url_rewriting.rb +39 -0
- data/lib/facebooker/rails/helpers/fb_connect.rb +89 -0
- data/lib/facebooker/rails/helpers.rb +762 -0
- data/lib/facebooker/rails/integration_session.rb +38 -0
- data/lib/facebooker/rails/profile_publisher_extensions.rb +42 -0
- data/lib/facebooker/rails/publisher.rb +526 -0
- data/lib/facebooker/rails/routing.rb +49 -0
- data/lib/facebooker/rails/test_helpers.rb +68 -0
- data/lib/facebooker/rails/utilities.rb +22 -0
- data/lib/facebooker/server_cache.rb +24 -0
- data/lib/facebooker/service.rb +94 -0
- data/lib/facebooker/session.rb +584 -0
- data/lib/facebooker/version.rb +9 -0
- data/lib/facebooker.rb +174 -0
- data/lib/net/http_multipart_post.rb +123 -0
- data/lib/tasks/facebooker.rake +18 -0
- data/lib/tasks/tunnel.rake +46 -0
- data/rails/init.rb +1 -0
- data/setup.rb +1585 -0
- data/templates/layout.erb +24 -0
- data/test/facebooker/adapters_test.rb +96 -0
- data/test/facebooker/admin_test.rb +102 -0
- data/test/facebooker/batch_request_test.rb +83 -0
- data/test/facebooker/data_test.rb +86 -0
- data/test/facebooker/logging_test.rb +43 -0
- data/test/facebooker/mobile_test.rb +45 -0
- data/test/facebooker/model_test.rb +123 -0
- data/test/facebooker/models/event_test.rb +15 -0
- data/test/facebooker/models/user_test.rb +295 -0
- data/test/facebooker/rails/publisher_test.rb +452 -0
- data/test/facebooker/rails_integration_test.rb +1312 -0
- data/test/facebooker/server_cache_test.rb +44 -0
- data/test/facebooker/session_test.rb +614 -0
- data/test/facebooker_test.rb +925 -0
- data/test/fixtures/multipart_post_body_with_only_parameters.txt +33 -0
- data/test/fixtures/multipart_post_body_with_single_file.txt +38 -0
- data/test/fixtures/multipart_post_body_with_single_file_that_has_nil_key.txt +38 -0
- data/test/net/http_multipart_post_test.rb +52 -0
- data/test/rails_test_helper.rb +11 -0
- data/test/test_helper.rb +66 -0
- metadata +217 -0
data/lib/facebooker.rb
ADDED
@@ -0,0 +1,174 @@
|
|
1
|
+
begin
|
2
|
+
unless Object.const_defined?("ActiveSupport") and ActiveSupport.const_defined?("JSON")
|
3
|
+
require 'json'
|
4
|
+
module Facebooker
|
5
|
+
def self.json_decode(str)
|
6
|
+
JSON.parse(str)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
else
|
10
|
+
module Facebooker
|
11
|
+
def self.json_decode(str)
|
12
|
+
ActiveSupport::JSON.decode(str)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
rescue
|
17
|
+
require 'json'
|
18
|
+
end
|
19
|
+
require 'zlib'
|
20
|
+
require 'digest/md5'
|
21
|
+
|
22
|
+
|
23
|
+
|
24
|
+
module Facebooker
|
25
|
+
|
26
|
+
class << self
|
27
|
+
|
28
|
+
def load_configuration(facebooker_yaml_file)
|
29
|
+
if File.exist?(facebooker_yaml_file)
|
30
|
+
if defined? RAILS_ENV
|
31
|
+
facebooker = YAML.load_file(facebooker_yaml_file)[RAILS_ENV]
|
32
|
+
else
|
33
|
+
facebooker = YAML.load_file(facebooker_yaml_file)
|
34
|
+
end
|
35
|
+
ENV['FACEBOOK_API_KEY'] = facebooker['api_key']
|
36
|
+
ENV['FACEBOOK_SECRET_KEY'] = facebooker['secret_key']
|
37
|
+
ENV['FACEBOOKER_RELATIVE_URL_ROOT'] = facebooker['canvas_page_name']
|
38
|
+
ENV['FACEBOOKER_API'] = facebooker['api']
|
39
|
+
if facebooker.has_key?('set_asset_host_to_callback_url')
|
40
|
+
Facebooker.set_asset_host_to_callback_url = facebooker['set_asset_host_to_callback_url']
|
41
|
+
end
|
42
|
+
Facebooker.timeout = facebooker['timeout']
|
43
|
+
if Object.const_defined?("ActionController")
|
44
|
+
ActionController::Base.asset_host = facebooker['callback_url'] if(ActionController::Base.asset_host.blank?) && Facebooker.set_asset_host_to_callback_url
|
45
|
+
end
|
46
|
+
@facebooker_configuration = facebooker
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def facebooker_config
|
51
|
+
@facebooker_configuration
|
52
|
+
end
|
53
|
+
|
54
|
+
def current_adapter=(adapter_class)
|
55
|
+
@current_adapter = adapter_class
|
56
|
+
end
|
57
|
+
|
58
|
+
def current_adapter
|
59
|
+
@current_adapter || Facebooker::AdapterBase.default_adapter
|
60
|
+
end
|
61
|
+
|
62
|
+
def load_adapter(params)
|
63
|
+
self.current_adapter = Facebooker::AdapterBase.load_adapter(params)
|
64
|
+
end
|
65
|
+
|
66
|
+
def facebook_path_prefix=(path)
|
67
|
+
current_adapter.facebook_path_prefix = path
|
68
|
+
end
|
69
|
+
|
70
|
+
# Default is canvas_page_name in yml file
|
71
|
+
def facebook_path_prefix
|
72
|
+
current_adapter.facebook_path_prefix
|
73
|
+
end
|
74
|
+
|
75
|
+
def is_for?(application_container)
|
76
|
+
current_adapter.is_for?(application_container)
|
77
|
+
end
|
78
|
+
|
79
|
+
def set_asset_host_to_callback_url=(val)
|
80
|
+
@set_asset_host_to_callback_url=val
|
81
|
+
end
|
82
|
+
|
83
|
+
def set_asset_host_to_callback_url
|
84
|
+
@set_asset_host_to_callback_url.nil? ? true : @set_asset_host_to_callback_url
|
85
|
+
end
|
86
|
+
|
87
|
+
def use_curl=(val)
|
88
|
+
@use_curl=val
|
89
|
+
end
|
90
|
+
|
91
|
+
def use_curl?
|
92
|
+
@use_curl
|
93
|
+
end
|
94
|
+
|
95
|
+
def timeout=(val)
|
96
|
+
@timeout = val.to_i
|
97
|
+
end
|
98
|
+
|
99
|
+
def timeout
|
100
|
+
@timeout
|
101
|
+
end
|
102
|
+
|
103
|
+
[:api_key,:secret_key, :www_server_base_url,:login_url_base,:install_url_base,:api_rest_path,:api_server_base,:api_server_base_url,:canvas_server_base, :video_server_base].each do |delegated_method|
|
104
|
+
define_method(delegated_method){ return current_adapter.send(delegated_method)}
|
105
|
+
end
|
106
|
+
|
107
|
+
|
108
|
+
def path_prefix
|
109
|
+
@path_prefix
|
110
|
+
end
|
111
|
+
|
112
|
+
|
113
|
+
# Set the asset path to the canvas path for just this one request
|
114
|
+
# by definition, we will make this a canvas request
|
115
|
+
def with_asset_path_for_canvas
|
116
|
+
original_asset_host = ActionController::Base.asset_host
|
117
|
+
begin
|
118
|
+
ActionController::Base.asset_host = Facebooker.api_server_base_url
|
119
|
+
request_for_canvas(true) do
|
120
|
+
yield
|
121
|
+
end
|
122
|
+
ensure
|
123
|
+
ActionController::Base.asset_host = original_asset_host
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
# If this request is_canvas_request
|
128
|
+
# then use the application name as the url root
|
129
|
+
def request_for_canvas(is_canvas_request)
|
130
|
+
original_path_prefix = @path_prefix
|
131
|
+
begin
|
132
|
+
@path_prefix = facebook_path_prefix if is_canvas_request
|
133
|
+
yield
|
134
|
+
ensure
|
135
|
+
@path_prefix = original_path_prefix
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
require 'facebooker/batch_request'
|
142
|
+
require 'facebooker/feed'
|
143
|
+
require 'facebooker/logging'
|
144
|
+
require 'facebooker/model'
|
145
|
+
require 'facebooker/parser'
|
146
|
+
require 'facebooker/service'
|
147
|
+
require 'facebooker/server_cache'
|
148
|
+
require 'facebooker/data'
|
149
|
+
require 'facebooker/admin'
|
150
|
+
require 'facebooker/mobile'
|
151
|
+
require 'facebooker/session'
|
152
|
+
require 'facebooker/version'
|
153
|
+
require 'facebooker/models/location'
|
154
|
+
require 'facebooker/models/affiliation'
|
155
|
+
require 'facebooker/models/album'
|
156
|
+
require 'facebooker/models/education_info'
|
157
|
+
require 'facebooker/models/work_info'
|
158
|
+
require 'facebooker/models/event'
|
159
|
+
require 'facebooker/models/group'
|
160
|
+
require 'facebooker/models/notifications'
|
161
|
+
require 'facebooker/models/page'
|
162
|
+
require 'facebooker/models/photo'
|
163
|
+
require 'facebooker/models/cookie'
|
164
|
+
require 'facebooker/models/applicationproperties'
|
165
|
+
require 'facebooker/models/applicationrestrictions'
|
166
|
+
require 'facebooker/models/tag'
|
167
|
+
require 'facebooker/models/user'
|
168
|
+
require 'facebooker/models/info_item'
|
169
|
+
require 'facebooker/models/info_section'
|
170
|
+
require 'facebooker/models/friend_list'
|
171
|
+
require 'facebooker/models/video'
|
172
|
+
require 'facebooker/adapters/adapter_base'
|
173
|
+
require 'facebooker/adapters/facebook_adapter'
|
174
|
+
require 'facebooker/adapters/bebo_adapter'
|
@@ -0,0 +1,123 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
|
3
|
+
module Net
|
4
|
+
class HTTP
|
5
|
+
class << self
|
6
|
+
def post_multipart_form(url, params)
|
7
|
+
MultipartPost.new(url, params).post
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
class MultipartPostFile
|
12
|
+
def initialize(filename=nil, content_type=nil, data=nil)
|
13
|
+
@filename = filename
|
14
|
+
@content_type = content_type
|
15
|
+
@data = data
|
16
|
+
end
|
17
|
+
|
18
|
+
attr_accessor :filename
|
19
|
+
attr_accessor :content_type
|
20
|
+
attr_accessor :data
|
21
|
+
end
|
22
|
+
|
23
|
+
class MultipartPost
|
24
|
+
def initialize(url, params)
|
25
|
+
@url = url
|
26
|
+
@multipart_post_files = extract_file_parameters_from(params)
|
27
|
+
@params = extract_non_file_parameters_from(params)
|
28
|
+
end
|
29
|
+
|
30
|
+
def post
|
31
|
+
req = Post.new(url.path)
|
32
|
+
req.body = body
|
33
|
+
req.content_type = content_type
|
34
|
+
req.basic_auth url.user, url.password if url.user
|
35
|
+
Net::HTTP.new(url.host, url.port).start {|http|
|
36
|
+
http.request(req)
|
37
|
+
}
|
38
|
+
end
|
39
|
+
|
40
|
+
BOUNDARY = "MichaelNiessnerIsSuperDuperAwesome"
|
41
|
+
|
42
|
+
protected
|
43
|
+
attr_reader :url, :params, :multipart_post_files
|
44
|
+
|
45
|
+
def extract_file_parameters_from(hash)
|
46
|
+
hash.reject{|key, value| !multipart_post_file?(value)}
|
47
|
+
end
|
48
|
+
|
49
|
+
def extract_non_file_parameters_from(hash)
|
50
|
+
hash.reject{|key, value| multipart_post_file?(value)}
|
51
|
+
end
|
52
|
+
|
53
|
+
def multipart_post_file?(object)
|
54
|
+
object.respond_to?(:content_type) &&
|
55
|
+
object.respond_to?(:data) &&
|
56
|
+
object.respond_to?(:filename)
|
57
|
+
end
|
58
|
+
|
59
|
+
def content_type
|
60
|
+
"multipart/form-data; boundary=#{BOUNDARY}"
|
61
|
+
end
|
62
|
+
|
63
|
+
def body
|
64
|
+
encode_parameters + encode_multipart_post_files + final_boundary
|
65
|
+
end
|
66
|
+
|
67
|
+
def encode_multipart_post_files
|
68
|
+
return "" if multipart_post_files.empty?
|
69
|
+
if multipart_post_files.size == 1
|
70
|
+
name = multipart_post_files.keys.first
|
71
|
+
file = multipart_post_files.values.first
|
72
|
+
encode_multipart_post_file(name, file)
|
73
|
+
else
|
74
|
+
raise "Currently more than 1 file upload is not supported."
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def encode_multipart_post_file(name, multipart_post_file)
|
79
|
+
parameter_boundary +
|
80
|
+
disposition_with_filename(name, multipart_post_file.filename) +
|
81
|
+
file_content_type(multipart_post_file.content_type) +
|
82
|
+
multipart_post_file.data +
|
83
|
+
"\r\n"
|
84
|
+
end
|
85
|
+
|
86
|
+
def encode_parameters
|
87
|
+
params.sort_by{|key, value| key.to_s}.map{|key, value| encode_parameter(key, value)}.join
|
88
|
+
end
|
89
|
+
|
90
|
+
def encode_parameter(key, value)
|
91
|
+
parameter_boundary + disposition_with_name(key) + value.to_s + "\r\n"
|
92
|
+
end
|
93
|
+
|
94
|
+
def file_content_type(string)
|
95
|
+
"Content-Type: #{string}\r\n\r\n"
|
96
|
+
end
|
97
|
+
|
98
|
+
def disposition_with_filename(name, filename)
|
99
|
+
if name.nil?
|
100
|
+
disposition("filename=\"#{filename}\"")
|
101
|
+
else
|
102
|
+
disposition("name=\"#{name}\"; filename=\"#{filename}\"")
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def disposition_with_name(name)
|
107
|
+
disposition("name=\"#{name}\"\r\n")
|
108
|
+
end
|
109
|
+
|
110
|
+
def disposition(attribute)
|
111
|
+
"Content-Disposition: form-data; #{attribute}\r\n"
|
112
|
+
end
|
113
|
+
|
114
|
+
def parameter_boundary
|
115
|
+
"--#{BOUNDARY}\r\n"
|
116
|
+
end
|
117
|
+
|
118
|
+
def final_boundary
|
119
|
+
"--#{BOUNDARY}--\r\n"
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
namespace :facebooker do
|
4
|
+
|
5
|
+
desc "Create a basic facebooker.yml configuration file"
|
6
|
+
task :setup => :environment do
|
7
|
+
facebook_config = File.join(RAILS_ROOT,"config","facebooker.yml")
|
8
|
+
unless File.exist?(facebook_config)
|
9
|
+
FileUtils.cp File.join(RAILS_ROOT,"vendor", "plugins", "facebooker", "facebooker.yml.tpl"), facebook_config
|
10
|
+
puts "Ensure 'GatewayPorts yes' is enabled in the remote development server's sshd config when using any of the facebooker:tunnel:*' rake tasks"
|
11
|
+
puts "Configuration created in #{RAILS_ROOT}/config/facebooker.yml"
|
12
|
+
else
|
13
|
+
puts "#{RAILS_ROOT}/config/facebooker.yml already exists"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
|
@@ -0,0 +1,46 @@
|
|
1
|
+
namespace :facebooker do
|
2
|
+
|
3
|
+
tunnel_ns = namespace :tunnel do
|
4
|
+
# Courtesy of Christopher Haupt
|
5
|
+
# http://www.BuildingWebApps.com
|
6
|
+
# http://www.LearningRails.com
|
7
|
+
desc "Create a reverse ssh tunnel from a public server to a private development server."
|
8
|
+
task :start => [ :environment, :config ] do
|
9
|
+
puts @notification
|
10
|
+
system @ssh_command
|
11
|
+
end
|
12
|
+
|
13
|
+
desc "Create a reverse ssh tunnel in the background. Requires ssh keys to be setup."
|
14
|
+
task :background_start => [ :environment, :config ] do
|
15
|
+
puts @notification
|
16
|
+
system "#{@ssh_command} > /dev/null 2>&1 &"
|
17
|
+
end
|
18
|
+
|
19
|
+
# Adapted from Evan Weaver: http://blog.evanweaver.com/articles/2007/07/13/developing-a-facebook-app-locally/
|
20
|
+
desc "Check if reverse tunnel is running"
|
21
|
+
task :status => [ :environment, :config ] do
|
22
|
+
if `ssh #{@public_host} -l #{@public_host_username} netstat -an |
|
23
|
+
egrep "tcp.*:#{@public_port}.*LISTEN" | wc`.to_i > 0
|
24
|
+
puts "Seems ok"
|
25
|
+
else
|
26
|
+
puts "Down"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
task :config => :environment do
|
31
|
+
facebook_config = File.dirname(__FILE__) + '/../../../../../config/facebooker.yml'
|
32
|
+
FACEBOOKER = YAML.load_file(facebook_config)[RAILS_ENV]
|
33
|
+
@public_host_username = FACEBOOKER['tunnel']['public_host_username']
|
34
|
+
@public_host = FACEBOOKER['tunnel']['public_host']
|
35
|
+
@public_port = FACEBOOKER['tunnel']['public_port']
|
36
|
+
@local_port = FACEBOOKER['tunnel']['local_port']
|
37
|
+
@ssh_port = FACEBOOKER['tunnel']['ssh_port'] || 22
|
38
|
+
@notification = "Starting tunnel #{@public_host}:#{@public_port} to 0.0.0.0:#{@local_port}"
|
39
|
+
@notification << " using SSH port #{@ssh_port}" unless @ssh_port == 22
|
40
|
+
# "GatewayPorts yes" needs to be enabled in the remote's sshd config
|
41
|
+
@ssh_command = "ssh -v -p #{@ssh_port} -nNT4 -R *:#{@public_port}:localhost:#{@local_port} #{@public_host_username}@#{@public_host}"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
desc "Create a reverse ssh tunnel from a public server to a private development server."
|
45
|
+
task :tunnel => tunnel_ns[:start]
|
46
|
+
end
|
data/rails/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__),'../init.rb')
|