flareshow 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1 @@
1
+ *.gem
data/Flareshow.gemspec ADDED
@@ -0,0 +1,82 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec`
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{flareshow}
8
+ s.version = "0.3.1"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Will Bailey"]
12
+ s.date = %q{2010-01-11}
13
+ s.description = %q{a ruby gem for interacting with the shareflow collaboration service by Zenbe}
14
+ s.email = %q{will.bailey@gmail.com}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE",
17
+ "README.md"
18
+ ]
19
+ s.files = [
20
+ ".gitignore",
21
+ "Flareshow.gemspec",
22
+ "LICENSE",
23
+ "README.md",
24
+ "Rakefile",
25
+ "TODO",
26
+ "VERSION",
27
+ "lib/cache.rb",
28
+ "lib/comment.rb",
29
+ "lib/exceptions.rb",
30
+ "lib/file_attachment.rb",
31
+ "lib/flareshow.rb",
32
+ "lib/flow.rb",
33
+ "lib/http_encoding_helper.rb",
34
+ "lib/invitation.rb",
35
+ "lib/membership.rb",
36
+ "lib/mime_parts.rb",
37
+ "lib/post.rb",
38
+ "lib/resource.rb",
39
+ "lib/searchable.rb",
40
+ "lib/server.rb",
41
+ "lib/service.rb",
42
+ "lib/user.rb",
43
+ "lib/util.rb",
44
+ "test/flareshow_test.rb",
45
+ "test/test_helper.rb"
46
+ ]
47
+ s.homepage = %q{http://github.com/willbailey/flareshow}
48
+ s.rdoc_options = ["--charset=UTF-8"]
49
+ s.require_paths = ["lib"]
50
+ s.rubyforge_project = %q{flareshow}
51
+ s.rubygems_version = %q{1.3.5}
52
+ s.summary = %q{a ruby gem for interacting with the shareflow collaboration service}
53
+ s.test_files = [
54
+ "test/flareshow_test.rb",
55
+ "test/test_helper.rb"
56
+ ]
57
+
58
+ if s.respond_to? :specification_version then
59
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
60
+ s.specification_version = 3
61
+
62
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
63
+ s.add_development_dependency(%q<thoughtbot-shoulda>, ["> 0"])
64
+ s.add_runtime_dependency(%q<json>, ["> 0"])
65
+ s.add_runtime_dependency(%q<curb>, ["> 0"])
66
+ s.add_runtime_dependency(%q<facets>, ["> 0"])
67
+ s.add_runtime_dependency(%q<uuid>, ["> 0"])
68
+ else
69
+ s.add_dependency(%q<thoughtbot-shoulda>, ["> 0"])
70
+ s.add_dependency(%q<json>, ["> 0"])
71
+ s.add_dependency(%q<curb>, ["> 0"])
72
+ s.add_dependency(%q<facets>, ["> 0"])
73
+ s.add_dependency(%q<uuid>, ["> 0"])
74
+ end
75
+ else
76
+ s.add_dependency(%q<thoughtbot-shoulda>, ["> 0"])
77
+ s.add_dependency(%q<json>, ["> 0"])
78
+ s.add_dependency(%q<curb>, ["> 0"])
79
+ s.add_dependency(%q<facets>, ["> 0"])
80
+ s.add_dependency(%q<uuid>, ["> 0"])
81
+ end
82
+ end
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Will Bailey
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.md ADDED
@@ -0,0 +1,122 @@
1
+ ___ ___ __
2
+ /'___\\_ \ /\ \
3
+ /\ \__///\ \ __ _ __ __ ____\ \ \___ ___ __ __ __
4
+ \ \ ,__\\ \ \ /'__`\ /\`'__\'__`\ /',__\\ \ _ `\ / __`\/\ \/\ \/\ \
5
+ \ \ \_/ \_\ \_/\ \L\.\_\ \ \/\ __//\__, `\\ \ \ \ \/\ \L\ \ \ \_/ \_/ \
6
+ \ \_\ /\____\ \__/.\_\\ \_\ \____\/\____/ \ \_\ \_\ \____/\ \___x___/'
7
+ \/_/ \/____/\/__/\/_/ \/_/\/____/\/___/ \/_/\/_/\/___/ \/__//__/
8
+
9
+ ~ a ruby wrapper for the Zenbe Shareflow API
10
+
11
+
12
+ ### Overview
13
+
14
+ Flareshow provides a ruby wrapper around the shareflow json rpc wire protocol.
15
+
16
+ For more information about the Shareflow API see:
17
+ <http://docs.google.com/View?id=dchr4h9d_5gz6nw3cb>
18
+
19
+ ### Getting Started
20
+ You'll need a shareflow account setup before you can begin interacting with the api. Go to <http://getshareflow.com> and sign up for a free account.
21
+
22
+ ### Authentication
23
+
24
+ All actions in Flareshow require authenticationFlareshow can automatically authenticate you against your shareflow server. Just create a YAML formatted .flareshowrc file in your home directory with the following keys
25
+
26
+ subdomain : demo.zenbe.com
27
+ login : demo
28
+ password : password
29
+
30
+ To authenticate manually do the following:
31
+
32
+ Flareshow::Service.configure(<subdomain>)
33
+ Flareshow.authenticate(<login>, <password>)
34
+
35
+ ### Usage
36
+
37
+ Once you've authenticated you can choose to use either interact directly with the Flareshow::Service or use the Flareshow::Resource domain models which encapsulate the domain logic of Shareflow providing a friendlier development environment.
38
+
39
+ ### Examples
40
+
41
+ #### accessing models and associations
42
+
43
+ Flareshow offers an ActiveRecord like syntax for retrieving and manipulating models.
44
+
45
+ # Get the first post from the server ordered by creation date by default
46
+ p = Post.first
47
+
48
+ # Get the comments for that post
49
+ p.comments
50
+
51
+ # get the file attachments for the post
52
+ files = p.files
53
+
54
+ # download a file
55
+ files.first.download
56
+
57
+
58
+ #### reading posts
59
+
60
+ # the following code will read all posts on the server in a loop
61
+ per_request = 30
62
+ results = []
63
+ offset = 0
64
+
65
+ loop do
66
+ posts = Post.find(:offset => offset, :limit => per_request)
67
+ results += posts
68
+ puts results.size
69
+ offset += per_request
70
+ break if posts.empty?
71
+ end
72
+
73
+ #### upload a file to a Post
74
+
75
+ p=Post.new()
76
+ p.save
77
+ p.create_file("/path/to/your/file")
78
+
79
+ #### searching for a post
80
+
81
+ # posts, files, comments include the searchable mixin
82
+ # and can be searched using full text search capabilities
83
+ # on the server
84
+ results = Post.search("some keywords")
85
+
86
+ #### deleting a post or comment
87
+
88
+ Post.first.destroy
89
+ Comment.first.destroy
90
+
91
+ #### renaming a flow
92
+
93
+ f=Flow.find_by_name('test')
94
+ f.name = 'a different name'
95
+ f.save
96
+
97
+ #### inviting someone to a flow (only available if you are the creator of the flow)
98
+
99
+ f=Flow.find_by_name('test')
100
+ f.send_invitations('test@test.com')
101
+
102
+ # this also works
103
+ f.send_invitations(['test1@test.com', 'test2@test.com'])
104
+
105
+ #### canceling an invitation to a flow (only available if you are the creator of the flow)
106
+
107
+ f=Flow.find_by_name('test')
108
+ # revoke an invitation by the email address that was invited
109
+ f.revoke_invitations('test@test.com')
110
+
111
+
112
+ #### removing a user from a flow (only available if you are the creator of the flow)
113
+
114
+ f = Flow.find_by_name('test')
115
+ users = f.list_users # get a listing of the users on the flow
116
+ # remove the last user in the list from the flow...you cant remove yourself in this way
117
+ f.remove_members(users.last.id)
118
+
119
+ #### listing all the users on a flow
120
+
121
+ f=Flow.first
122
+ f.list_users
data/Rakefile ADDED
@@ -0,0 +1,60 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "flareshow"
8
+ gem.summary = %Q{a ruby gem for interacting with the shareflow collaboration service}
9
+ gem.description = %Q{a ruby gem for interacting with the shareflow collaboration service by Zenbe}
10
+ gem.email = "will.bailey@gmail.com"
11
+ gem.homepage = "http://github.com/willbailey/flareshow"
12
+ gem.authors = ["Will Bailey"]
13
+ gem.add_development_dependency "thoughtbot-shoulda", "> 0"
14
+ gem.add_dependency "json", "> 0"
15
+ gem.add_dependency "curb", "> 0"
16
+ gem.add_dependency "facets", "> 0"
17
+ gem.add_dependency "uuid", "> 0"
18
+ gem.rubyforge_project = "flareshow"
19
+ end
20
+ rescue LoadError
21
+ puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
22
+ end
23
+
24
+ require 'rake/testtask'
25
+ Rake::TestTask.new(:test) do |test|
26
+ test.libs << 'lib' << 'test'
27
+ test.pattern = 'test/**/*_test.rb'
28
+ test.verbose = true
29
+ end
30
+
31
+ begin
32
+ require 'rcov/rcovtask'
33
+ Rcov::RcovTask.new do |test|
34
+ test.libs << 'test'
35
+ test.pattern = 'test/**/*_test.rb'
36
+ test.verbose = true
37
+ end
38
+ rescue LoadError
39
+ task :rcov do
40
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
41
+ end
42
+ end
43
+
44
+ task :test => :check_dependencies
45
+
46
+ task :default => :test
47
+
48
+ require 'rake/rdoctask'
49
+ Rake::RDocTask.new do |rdoc|
50
+ if File.exist?('VERSION')
51
+ version = File.read('VERSION')
52
+ else
53
+ version = ""
54
+ end
55
+
56
+ rdoc.rdoc_dir = 'rdoc'
57
+ rdoc.title = "flareshow #{version}"
58
+ rdoc.rdoc_files.include('README*')
59
+ rdoc.rdoc_files.include('lib/**/*.rb')
60
+ end
data/TODO ADDED
@@ -0,0 +1,14 @@
1
+ TODO:
2
+ shareflow api docs
3
+ write tests with Fakeweb
4
+ flesh out more resource methods
5
+ make post requests with embedded file attachments respond with the file attachments for now...later we need to allow the creation of files in the same way as posts as flat
6
+ namespaced objects
7
+ handle failure states on requests
8
+ namespace auth requests to be consistent with other api calls
9
+
10
+ TODONE:
11
+ remove temporary auth setup logging in user automatically
12
+
13
+ QUESTIONS:
14
+ should authentication responses look like query and commit responses namespacing is applied differently in authentication currently?
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.3.1
data/lib/cache.rb ADDED
@@ -0,0 +1,91 @@
1
+ # provides an interface for various
2
+ # caches that flareshow might use
3
+ class Flareshow::CacheManager
4
+
5
+ class << self
6
+ # assimilate the resources provided in the response
7
+ def assimilate_resources(data)
8
+ # process each resource key and generate a new object
9
+ # or merge the object data with an existing object
10
+ data.inject({}) do |memo,resource_pair|
11
+ resource_key, resources = resource_pair[0], resource_pair[1]
12
+
13
+ fs_resource_array = memo[resource_key] ||= []
14
+ klass_name = Flareshow::ResourceToClassMap[resource_key]
15
+ klass = Kernel.const_get(klass_name)
16
+ if klass_name
17
+ if klass
18
+ resources.each do |resource_data|
19
+ item = cache.get_resource(resource_key, resource_data["id"])
20
+ if item
21
+ item.update(resource_data, :server)
22
+ else
23
+ item = klass.new(resource_data, :server)
24
+ end
25
+ cache.set_resource(resource_key, item.id, item)
26
+ fs_resource_array << item
27
+ end
28
+ end
29
+ end
30
+ memo
31
+ end
32
+ end
33
+
34
+ # get the managed cache object
35
+ def cache
36
+ @cache ||= Flareshow::Cache.new
37
+ end
38
+
39
+ end
40
+
41
+
42
+ end
43
+
44
+ # a simple in memory cache for Flareshow objects
45
+ class Flareshow::Cache
46
+
47
+ # you can create your own cache object and plug it into the cache manager.
48
+ # The cache class should implement the public methods
49
+ # get_resource, set_resource, delete_resource, list_resource, flush and size
50
+
51
+ # load a resource from the cache
52
+ def get_resource(resource_key, id)
53
+ resource_cache(resource_key)[id]
54
+ end
55
+
56
+ # set a resource in the cache
57
+ def set_resource(resource_key, id, object)
58
+ resource_cache(resource_key)[id] = object
59
+ end
60
+
61
+ # remove a resource
62
+ def delete_resource(resource_key, id)
63
+ resource_cache(resource_key).delete(id)
64
+ end
65
+
66
+ def list_resource(resource_key)
67
+ resource_cache(resource_key)
68
+ end
69
+
70
+ # remove all cached objects
71
+ def flush
72
+ data = {}
73
+ end
74
+
75
+ # number of cached objects
76
+ def size
77
+ data.values.inject(0){|m,v| m+=v.size}
78
+ end
79
+
80
+ private
81
+ # data store for the cache
82
+ def data
83
+ @cache ||= {}
84
+ end
85
+
86
+ # get a specific resource dictionary
87
+ def resource_cache(resource_key)
88
+ data[resource_key] ||= Dictionary.new
89
+ end
90
+
91
+ end
data/lib/comment.rb ADDED
@@ -0,0 +1,28 @@
1
+ class Comment < Flareshow::Resource
2
+
3
+ @attr_accessible = [:content, :post_id]
4
+ @attr_required = [:post_id, :content]
5
+
6
+ extend Flareshow::Searchable
7
+
8
+ # permalink to this comment
9
+ def permalink(mobile=false)
10
+ if mobile
11
+ "http://#{Flareshow::Service.server.host}/#{Flareshow::Service.server.domain}/shareflow/mobile/post/#{reply_to}"
12
+ else
13
+ "http://#{Flareshow::Service.server.host}/#{Flareshow::Service.server.domain}/shareflow/p/#{reply_to}?comment_id#{id}"
14
+ end
15
+ end
16
+
17
+ # get the post for this comment
18
+ def post
19
+ Post.first(:id => post_id)
20
+ end
21
+
22
+ # user for this post
23
+ def user
24
+ return User.current unless user_id
25
+ User.first({:id => user_id})
26
+ end
27
+
28
+ end
data/lib/exceptions.rb ADDED
@@ -0,0 +1,32 @@
1
+ module Flareshow
2
+
3
+ class MissingRequiredField < Exception
4
+ def message
5
+ "you attempted to save an object without providing all of the required fields"
6
+ end
7
+ end
8
+
9
+ class APIAccessException < Exception
10
+ def message
11
+ "you've attempted to change an object in a way not permitted by the API"
12
+ end
13
+ end
14
+
15
+ class ConfigurationException < Exception
16
+ def message
17
+ "the shareflow service connection has not been configured properly"
18
+ end
19
+ end
20
+
21
+ class AuthenticationRequired < Exception
22
+ def message
23
+ "this action requires authentication"
24
+ end
25
+ end
26
+
27
+ class AuthenticationFailed < Exception
28
+ def message
29
+ "authentication failed"
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,40 @@
1
+ class FileAttachment < Flareshow::Resource
2
+
3
+ @read_only=true
4
+
5
+ extend Flareshow::Searchable
6
+
7
+ # =================
8
+ # = Class Methods =
9
+ # =================
10
+ class << self
11
+ # file attachments has a resource key of files
12
+ # for querying the server
13
+ def resource_key
14
+ "files"
15
+ end
16
+ end
17
+
18
+ # download the file contents
19
+ def download
20
+ url = self.url
21
+ unless url.match(/http/)
22
+ url = "http://#{Flareshow::Service.server.host}/#{Flareshow::Service.server.domain}#{url}"
23
+ end
24
+ Flareshow::Util.log_info("getting #{url}")
25
+ Flareshow::Service.http_get(url)
26
+ end
27
+
28
+ # post for this file
29
+ def post
30
+ return false unless post_id
31
+ Post.find({:id => post_id})
32
+ end
33
+
34
+ # get the user for this file
35
+ def user
36
+ p = post && post.first
37
+ p.user && p.user.first
38
+ end
39
+
40
+ end
data/lib/flareshow.rb ADDED
@@ -0,0 +1,61 @@
1
+ ROOT = File.dirname(__FILE__) unless defined? ROOT
2
+
3
+ # gems
4
+ require 'rubygems' #TODO fix
5
+ require 'json'
6
+ require 'facets'
7
+ require 'facets/dictionary'
8
+ require 'uuid'
9
+ require 'mime/types'
10
+ require 'cgi'
11
+
12
+ # std lib
13
+ require 'net/http'
14
+ require 'net/https'
15
+ require 'ostruct'
16
+ require 'yaml'
17
+ require 'logger'
18
+ require 'ruby-debug'
19
+
20
+ # Application Constants and configuration
21
+ module Flareshow
22
+
23
+ # mappings to allow easy conversion from the
24
+ # response keys the server sends back in JSUP
25
+ # messages
26
+ ResourceToClassMap = {
27
+ "flows" => "Flow",
28
+ "posts" => "Post",
29
+ "comments" => "Comment",
30
+ "files" => "FileAttachment",
31
+ "memberships" => "Membership",
32
+ "invitations" => "Invitation",
33
+ "users" => "User"
34
+ } unless defined? ResourceToClassMap
35
+ ClassToResourceMap = ResourceToClassMap.invert unless defined? ClassToResourceMap
36
+ end
37
+
38
+ # app
39
+ require File.join(ROOT, 'service')
40
+ require File.join(ROOT, 'resource')
41
+ require File.join(ROOT, 'cache')
42
+ require File.join(ROOT, 'searchable')
43
+
44
+ # logging
45
+ DEFAULT_LOGGER = Logger.new(STDOUT) unless defined?(DEFAULT_LOGGER)
46
+ DEFAULT_LOGGER.level = Logger::ERROR
47
+ Dir.glob(File.join(ROOT, "*.rb")).each{|lib| require lib}
48
+
49
+ # check for presence of config file
50
+ config_file_path = File.expand_path("~/.flareshowrc")
51
+ if File.exists?(config_file_path) && !Flareshow::Service.authenticated?
52
+ data = YAML.load_file(config_file_path)
53
+ Flareshow::Service.debug_output=true if data["debug_http"]
54
+ host = data["host"] || "biz.zenbe.com"
55
+ subdomain = data["subdomain"]
56
+ DEFAULT_LOGGER.level = data["log_level"].to_i if data["log_level"]
57
+ Flareshow::Service.configure(subdomain, host)
58
+ if data["login"] && data["password"]
59
+ User.log_in(data["login"], data["password"])
60
+ end
61
+ end
data/lib/flow.rb ADDED
@@ -0,0 +1,78 @@
1
+ class Flow < Flareshow::Resource
2
+
3
+ @attr_accessible = [:name, :remove_members, :uninvite, :invite]
4
+ @attr_required = [:name]
5
+
6
+ # =================
7
+ # = Class Methods =
8
+ # =================
9
+ class << self
10
+ # find a flow by name
11
+ def find_by_name(name)
12
+ self.first({:name => name})
13
+ end
14
+ end
15
+
16
+ # permalink for this flow
17
+ def permalink(mobile=false)
18
+ if mobile
19
+ "http://#{Flareshow::Service.server.host}/#{Flareshow::Service.server.domain}/shareflow/mobile/flows/#{id}"
20
+ else
21
+ "http://#{Flareshow::Service.server.host}/#{Flareshow::Service.server.domain}/shareflow/c/#{id}"
22
+ end
23
+ end
24
+
25
+ #invite/reinvite a user to a flow by email address
26
+ def send_invitations(email_addresses)
27
+ self.invite = [email_addresses].flatten
28
+ self.save
29
+ end
30
+
31
+ # uninvite an invited user from a flow by email address
32
+ def revoke_invitations(email_addresses)
33
+ self.uninvite = [email_addresses].flatten
34
+ self.save
35
+ end
36
+
37
+ # remove a user from a flow
38
+ # you must be the owner of the flow to perform
39
+ # this action
40
+ def remove_members(member_ids)
41
+ self.remove_members = [member_ids].flatten
42
+ self.save
43
+ end
44
+
45
+ # build a new post but don't persist it immediatel
46
+ def build_post(attributes)
47
+ post = Post.new
48
+ post.update(attributes)
49
+ post.flow_id = id
50
+ post
51
+ end
52
+
53
+ # create a new post in this flow
54
+ def create_post(attributes)
55
+ p=build_post(attributes)
56
+ p.save
57
+ end
58
+
59
+ # posts for this flow...only returns the first 100 (API max)
60
+ # to load all posts you can interact with the Service class directly
61
+ # to loop through in batches of 100 using the offset parameter or
62
+ # create a flow object and override this method with the looping logic
63
+ def posts
64
+ Post.find(:flow_id => id)
65
+ end
66
+
67
+ # list the invitations to this flow
68
+ def list_invitations
69
+ Invitation.find(:flow_id => id)
70
+ end
71
+
72
+ # list all users on the flow currently
73
+ def list_users
74
+ response = Flareshow::Service.query({:flows=>{:id => self.id, :limit => 1, :include=>['users']}})
75
+ (Flareshow::CacheManager.assimilate_resources(response["resources"]) || {})["users"]
76
+ end
77
+
78
+ end
@@ -0,0 +1,49 @@
1
+ # Intended to extend the Net::HTTP response object
2
+ # and adds support for decoding gzip and deflate encoded pages
3
+ #
4
+ # Author: Jason Stirk <http://griffin.oobleyboo.com>
5
+ # Home: http://griffin.oobleyboo.com/projects/http_encoding_helper
6
+ # Created: 5 September 2007
7
+ # Last Updated: 23 November 2007
8
+ #
9
+ # Usage:
10
+ #
11
+ # require 'net/http'
12
+ # require 'http_encoding_helper'
13
+ # headers={'Accept-Encoding' => 'gzip, deflate' }
14
+ # http = Net::HTTP.new('griffin.oobleyboo.com', 80)
15
+ # http.start do |h|
16
+ # request = Net::HTTP::Get.new('/', headers)
17
+ # response = http.request(request)
18
+ # content=response.plain_body # Method from our library
19
+ # puts "Transferred: #{response.body.length} bytes"
20
+ # puts "Compression: #{response['content-encoding']}"
21
+ # puts "Extracted: #{response.plain_body.length} bytes"
22
+ # end
23
+ #
24
+
25
+ require 'zlib'
26
+ require 'stringio'
27
+
28
+ class Net::HTTPResponse
29
+ # Return the uncompressed content
30
+ def plain_body
31
+ encoding=self['content-encoding']
32
+ content=nil
33
+ if encoding then
34
+ case encoding
35
+ when 'gzip'
36
+ i=Zlib::GzipReader.new(StringIO.new(self.body))
37
+ content=i.read
38
+ when 'deflate'
39
+ i=Zlib::Inflate.new
40
+ content=i.inflate(self.body)
41
+ else
42
+ raise "Unknown encoding - #{encoding}"
43
+ end
44
+ else
45
+ content=self.body
46
+ end
47
+ return content
48
+ end
49
+ end