fluther 1.0.0

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/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
data/lib/fluther.rb ADDED
@@ -0,0 +1,6 @@
1
+ require 'fluther/config'
2
+ require 'fluther/proxy'
3
+
4
+ module Fluther
5
+ ClientVersion = '1.0.2'.freeze
6
+ end
@@ -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
metadata ADDED
@@ -0,0 +1,80 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fluther
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 1
7
+ - 0
8
+ - 0
9
+ version: 1.0.0
10
+ platform: ruby
11
+ authors:
12
+ - Steve Sloan
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-08-10 00:00:00 -07:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: em-http-request
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ segments:
29
+ - 0
30
+ version: "0"
31
+ type: :runtime
32
+ version_requirements: *id001
33
+ description: Ruby interface to the Fluther discussion system
34
+ email: steve@conceivian.com
35
+ executables: []
36
+
37
+ extensions: []
38
+
39
+ extra_rdoc_files:
40
+ - README.textile
41
+ files:
42
+ - MIT-LICENSE
43
+ - README.textile
44
+ - lib/fluther.rb
45
+ - lib/fluther/config.rb
46
+ - lib/fluther/proxy.rb
47
+ has_rdoc: true
48
+ homepage: http://github.com/conceivian/fluther
49
+ licenses: []
50
+
51
+ post_install_message:
52
+ rdoc_options:
53
+ - --charset=UTF-8
54
+ require_paths:
55
+ - lib
56
+ required_ruby_version: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ segments:
62
+ - 0
63
+ version: "0"
64
+ required_rubygems_version: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ segments:
70
+ - 0
71
+ version: "0"
72
+ requirements: []
73
+
74
+ rubyforge_project:
75
+ rubygems_version: 1.3.7
76
+ signing_key:
77
+ specification_version: 3
78
+ summary: Ruby interface to the Fluther discussion system
79
+ test_files: []
80
+