justinchen-fluther 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/MIT-LICENSE +20 -0
- data/README.textile +104 -0
- data/lib/fluther/config.rb +21 -0
- data/lib/fluther/proxy.rb +110 -0
- data/lib/fluther.rb +6 -0
- metadata +84 -0
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2009 Plataforma Tecnologia. http://blog.plataformatec.com.br
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.textile
ADDED
@@ -0,0 +1,104 @@
|
|
1
|
+
h1. Fluther Ruby Client
|
2
|
+
|
3
|
+
h2. Introduction
|
4
|
+
|
5
|
+
This gem provides an interface to the "Fluther discussion service":http://www.fluther.com/. It is implemented as a piece of Rack
|
6
|
+
middleware which handles proxying requests to Fluther and returning the response so that it can be included in your web application.
|
7
|
+
While it should be usable with any Rack-based application, these docs assume you are embedding Fluther in a Rails 2.x application.
|
8
|
+
|
9
|
+
Requirements:
|
10
|
+
* Rack
|
11
|
+
* Warden
|
12
|
+
* Thin (optional)
|
13
|
+
|
14
|
+
h2. Installation
|
15
|
+
|
16
|
+
* Add the @fluther@ gem to your application (i.e. in @Gemfile@ or @environment.rb@).
|
17
|
+
|
18
|
+
* Create an initializer (e.g. @config/initializers/fluther.rb@) that inserts the Fluther proxy after the Warden module,
|
19
|
+
mounting it on the appropriate path:
|
20
|
+
|
21
|
+
<pre>
|
22
|
+
Rails.configuration.after_initialize do
|
23
|
+
Rails.configuration.middleware.insert_after Warden::Manager, Fluther::Proxy, '/qna'
|
24
|
+
end
|
25
|
+
</pre>
|
26
|
+
|
27
|
+
* In the environment or initializer, add the Fluther configuration:
|
28
|
+
|
29
|
+
<pre>
|
30
|
+
class Fluther::Config
|
31
|
+
# hostname of the Fluther server for this environment (provided by Fluther)
|
32
|
+
fluther_host 'fstage.fluther.com'
|
33
|
+
|
34
|
+
# federated API key (provided by Fluther)
|
35
|
+
app_key '2b6a0009c414c53e3d4fa8f8c3134d59'
|
36
|
+
|
37
|
+
# mapping of attributes in the User model to the Fluther user
|
38
|
+
user_fields :id => :id, :name => :name, :email => :email # (defaults)
|
39
|
+
end
|
40
|
+
</pre>
|
41
|
+
|
42
|
+
|
43
|
+
h2. Rails Integration
|
44
|
+
|
45
|
+
The proxy provides three Rack variables that include the Fluther response: @fluther.header@, @fluther.title@, and
|
46
|
+
@fluther.response@. The first two (may) contain HTML blocks which should be inserted into the page @<head>@ and @<title>@ blocks,
|
47
|
+
respectively, and the third is the HTML for the Fluther widget itself.
|
48
|
+
|
49
|
+
To integrate the response into your application, you should add an action which is routed from the same path as the Fluther proxy.
|
50
|
+
For this example, we assume the controller is @MainController@, the action is @fluther@, and as above, it is mounted at @/qna@.
|
51
|
+
Also, we assume that the application layout includes @yield(:head)@ in the @<head>@ block:
|
52
|
+
|
53
|
+
<pre>
|
54
|
+
# config/routes.rb
|
55
|
+
map.fluther '/qna/*_', :controller => 'main', :action => 'fluther'
|
56
|
+
</pre>
|
57
|
+
|
58
|
+
<pre>
|
59
|
+
# app/views/main/fluther.html.erb
|
60
|
+
<%
|
61
|
+
if (header = request.env['fluther.header']).present?
|
62
|
+
content_for :head, header
|
63
|
+
end
|
64
|
+
if (title = request.env['fluther.title']).present?
|
65
|
+
content_for :head, content_tag(:title, title)
|
66
|
+
end
|
67
|
+
%>
|
68
|
+
<%= request.env['fluther.response'] -%>
|
69
|
+
</pre>
|
70
|
+
|
71
|
+
You should now be able to start your application, navigate to http://localhost:300/qna and see the Fluther page.
|
72
|
+
|
73
|
+
|
74
|
+
h2. Thin Integration
|
75
|
+
|
76
|
+
The Fluther gem uses "EventMachine":http://github.com/igrigorik/em-http-request to perform the HTTP request. When running under a
|
77
|
+
non-EventMachine web server, the proxy request will be performed synchronously. However, when running under
|
78
|
+
the "Thin":http://code.macournoyer.com/thin/ web server, the proxy can take advantage of the presense of EventMachine to perform the
|
79
|
+
request asynchronously, i.e. without blocking the web server process. This does require some additional setup:
|
80
|
+
|
81
|
+
* Add the @async-rack@ gem to your application.
|
82
|
+
* Add the following Rails code (v2 specific) to the block in the initializer you created above:
|
83
|
+
|
84
|
+
<pre>
|
85
|
+
...
|
86
|
+
Rails.configuration.threadsafe!
|
87
|
+
Rails.configuration.dependency_loading = true
|
88
|
+
class AsyncRack::Lock # kludge to disable error on async response in Rack::Lock
|
89
|
+
def async_callback(result)
|
90
|
+
super
|
91
|
+
end
|
92
|
+
end
|
93
|
+
</pre>
|
94
|
+
|
95
|
+
* To run your application: @script/server thin@
|
96
|
+
|
97
|
+
|
98
|
+
h2. Credits
|
99
|
+
|
100
|
+
Special thanks to Andrew and Ben at Fluther for all their help with integration and testing.
|
101
|
+
|
102
|
+
* Author: "Steve Sloan":mailto:steve@conceivian.com
|
103
|
+
* Copyright: (c) 2010 Conceivian Corporation
|
104
|
+
* License: MIT
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Fluther
|
2
|
+
class Config
|
3
|
+
def self.fluther_host( *args )
|
4
|
+
@@fluther_host = nil unless defined? @@fluther_host
|
5
|
+
return @@fluther_host if args.empty?
|
6
|
+
@@fluther_host = args.first
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.app_key( *args )
|
10
|
+
@@app_key = nil unless defined? @@app_key
|
11
|
+
return @@app_key if args.empty?
|
12
|
+
@@app_key = args.first
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.user_fields( *args )
|
16
|
+
@@user_fields = { :id => :id, :name => :name, :email => :email } unless defined? @@user_fields
|
17
|
+
return @@user_fields if args.empty?
|
18
|
+
@@user_fields.update args.first
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
require 'rack/request'
|
2
|
+
require 'em-http-request'
|
3
|
+
|
4
|
+
module Fluther
|
5
|
+
|
6
|
+
class Proxy
|
7
|
+
def initialize( app, prefix )
|
8
|
+
@app, @prefix = app, prefix
|
9
|
+
@prefix = "/#{@prefix}" unless @prefix.starts_with?('/')
|
10
|
+
end
|
11
|
+
|
12
|
+
def call( env )
|
13
|
+
return @app.call( env ) unless env['PATH_INFO'] =~ %r{^#{@prefix}}
|
14
|
+
@in_req = Rack::Request.new env
|
15
|
+
|
16
|
+
@user = {}
|
17
|
+
if user = @in_req.env['warden'].authenticate rescue nil
|
18
|
+
@user = Hash[ Fluther::Config.user_fields.map { |dest, src| [dest, user.send(src).to_s] } ]
|
19
|
+
end
|
20
|
+
|
21
|
+
exec_request
|
22
|
+
end
|
23
|
+
|
24
|
+
protected
|
25
|
+
def build_request
|
26
|
+
params = @in_req.params.dup.update(
|
27
|
+
:fed_key => Fluther::Config.app_key
|
28
|
+
)
|
29
|
+
params[:fed_sessionid] = @in_req.cookies['fed_sessionid'] if @in_req.cookies['fed_sessionid']
|
30
|
+
if @user.present?
|
31
|
+
params[:fed_uid] = @user[:id].to_s
|
32
|
+
params[:fed_username] = @user[:name].to_s
|
33
|
+
params[:fed_email] = @user[:email].to_s
|
34
|
+
end
|
35
|
+
|
36
|
+
options = {
|
37
|
+
:redirects => 0,
|
38
|
+
:timeout => 10,
|
39
|
+
:head => {
|
40
|
+
'User-Agent' => "Fluther Federated Client #{Fluther::ClientVersion} (Ruby)",
|
41
|
+
'X-Forwarded-For' => @in_req.env['REMOTE_ADDR'],
|
42
|
+
'X-Forwarded-Host' => @in_req.env['HTTP_HOST'],
|
43
|
+
}
|
44
|
+
}
|
45
|
+
options[:head]['X-Requested-With'] = @in_req.env['HTTP_X_REQUESTED_WITH'] if @in_req.env['HTTP_X_REQUESTED_WITH']
|
46
|
+
options[@in_req.post? ? :body : :query] = params
|
47
|
+
|
48
|
+
path = @in_req.path.sub( %r{^#{@prefix}}, '' )
|
49
|
+
path = '/' + path unless path.starts_with?('/')
|
50
|
+
url = "#{@in_req.scheme}://#{Fluther::Config.fluther_host}#{path}"
|
51
|
+
|
52
|
+
Rails.logger.debug @in_req.request_method
|
53
|
+
Rails.logger.debug url
|
54
|
+
Rails.logger.debug options
|
55
|
+
|
56
|
+
EventMachine::HttpRequest.new( url ).send( @in_req.request_method.downcase.to_sym, options )
|
57
|
+
end
|
58
|
+
|
59
|
+
def exec_request
|
60
|
+
result = nil
|
61
|
+
em_running = EM.reactor_running?
|
62
|
+
EM.run do
|
63
|
+
fluther = build_request
|
64
|
+
fluther.callback do
|
65
|
+
result = handle_response fluther
|
66
|
+
|
67
|
+
if em_running
|
68
|
+
# async
|
69
|
+
@in_req.env['async.callback'].call result
|
70
|
+
else
|
71
|
+
# sync
|
72
|
+
EM.stop
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
if em_running
|
78
|
+
# async
|
79
|
+
throw :async
|
80
|
+
else
|
81
|
+
# sync
|
82
|
+
result
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def handle_response( fluther )
|
87
|
+
Rails.logger.debug fluther.response_header.status
|
88
|
+
Rails.logger.debug fluther.response_header
|
89
|
+
|
90
|
+
type_header = fluther.response_header['CONTENT_TYPE']
|
91
|
+
content_type = type_header.split(';')[0] || 'text/html'
|
92
|
+
|
93
|
+
result = if [301, 302].include?( fluther.response_header.status )
|
94
|
+
[ fluther.response_header.status, {'Location' => fluther.response_header['LOCATION']}, ['Redirecting'] ]
|
95
|
+
|
96
|
+
elsif @in_req.xhr? || (content_type != 'text/html')
|
97
|
+
[ fluther.response_header.status, {'Content-Type' => type_header}, [fluther.response] ]
|
98
|
+
|
99
|
+
else
|
100
|
+
fluther.response.html_safe if fluther.response.respond_to?(:html_safe)
|
101
|
+
@in_req.env['fluther.response'] = fluther.response
|
102
|
+
@in_req.env['fluther.title'] = fluther.response_header['FLUTHER_TITLE'] if fluther.response_header['FLUTHER_TITLE']
|
103
|
+
@in_req.env['fluther.header'] = fluther.response_header['FLUTHER_HEADER'] if fluther.response_header['FLUTHER_HEADER']
|
104
|
+
@app.call @in_req.env
|
105
|
+
end
|
106
|
+
result
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
end
|
data/lib/fluther.rb
ADDED
metadata
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: justinchen-fluther
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 23
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 1
|
8
|
+
- 0
|
9
|
+
- 0
|
10
|
+
version: 1.0.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Steve Sloan
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2010-08-10 00:00:00 -07:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: em-http-request
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 3
|
30
|
+
segments:
|
31
|
+
- 0
|
32
|
+
version: "0"
|
33
|
+
type: :runtime
|
34
|
+
version_requirements: *id001
|
35
|
+
description: Ruby interface to the Fluther discussion system
|
36
|
+
email: steve@conceivian.com
|
37
|
+
executables: []
|
38
|
+
|
39
|
+
extensions: []
|
40
|
+
|
41
|
+
extra_rdoc_files:
|
42
|
+
- README.textile
|
43
|
+
files:
|
44
|
+
- MIT-LICENSE
|
45
|
+
- README.textile
|
46
|
+
- lib/fluther.rb
|
47
|
+
- lib/fluther/config.rb
|
48
|
+
- lib/fluther/proxy.rb
|
49
|
+
has_rdoc: true
|
50
|
+
homepage: http://github.com/conceivian/fluther
|
51
|
+
licenses: []
|
52
|
+
|
53
|
+
post_install_message:
|
54
|
+
rdoc_options:
|
55
|
+
- --charset=UTF-8
|
56
|
+
require_paths:
|
57
|
+
- lib
|
58
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
59
|
+
none: false
|
60
|
+
requirements:
|
61
|
+
- - ">="
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
hash: 3
|
64
|
+
segments:
|
65
|
+
- 0
|
66
|
+
version: "0"
|
67
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
68
|
+
none: false
|
69
|
+
requirements:
|
70
|
+
- - ">="
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
hash: 3
|
73
|
+
segments:
|
74
|
+
- 0
|
75
|
+
version: "0"
|
76
|
+
requirements: []
|
77
|
+
|
78
|
+
rubyforge_project:
|
79
|
+
rubygems_version: 1.3.7
|
80
|
+
signing_key:
|
81
|
+
specification_version: 3
|
82
|
+
summary: Ruby interface to the Fluther discussion system
|
83
|
+
test_files: []
|
84
|
+
|